我应该何时定义自己的副本ctor和赋值运算符

时间:2010-02-27 15:57:04

标签: c++

我正在阅读第5项中的有效C ++,它提到了我必须自己定义复制赋值运算符的两种情况。该案例是一个包含const和引用成员的类。

我写信是为了询问我必须定义自己的复制构造函数和赋值运算符的一般规则或情况是什么?

我还想知道何时必须定义自己的构造函数和析构函数。

非常感谢!

2 个答案:

答案 0 :(得分:5)

在以下情况下,您必须创建自己的复制构造函数和赋值运算符(通常也是默认构造函数):

  • 您希望复制或分配对象,或将其放入标准容器,例如vector
  • 默认的复制构造函数和赋值运算符不会执行正确的操作。

请考虑以下代码:

class A; // defined elsewhere
class B {
private:
    A *my_very_own_a;
};

如果让自动复制构造函数复制B,它将复制A *指针值,以便复制指向与原始版本相同的A实例。但是这个类的设计的一部分是每个B都有自己的A,所以自动复制构造函数已经打破了这个契约。因此,您需要编写自己的复制构造函数,为新的A创建一个新的B指向。

但是,请考虑这种情况:

class A; // defined elsewhere
class B {
private:
    A *shared_reference_to_a;
};

此处每个B都包含指向A的指针,但类合约不要求每个A使用唯一B。所以自动拷贝构造函数可能会在这里做正确的事。

请注意,这两个示例都是相同的代码,但具有不同的设计意图

第一种情况的示例可能是B ==对话框,A ==按钮。如果您创建对话框的副本,它可能还需要所有内容的副本。

第二个示例可能是B ==对话框,A ==对窗口管理器的引用。如果复制对话框,则副本可能与原始文件存在于同一窗口管理器中。

答案 1 :(得分:3)

默认的复制构造函数通常会导致两个对象“指向”一个共同的内存。这是因为所有默认的复制构造函数都是成员分配。因此,如果你有一个指针成员,这可能会让你陷入麻烦

    CClass::CClass(const CClass& src)
    {
       // these two point to the same place in memory now
       // you really aught to allocate new memory
       m_ptr = src.m_ptr;

       // not a big deal for members that aren't "pointing" elsewhere
       // this copy works great
       m_int = src.m_int;
    }

你基本上不希望最终出现两个实例“指向”内存中相同位置的情况。当指针作为成员指向动态分配的内存时,就会发生这种情况。引用也可能发生这种情况,都引用了类本身之外的东西。文件句柄可能会发生这种情况,您可能需要复制/重新打开新文件。

事实上,后一种情况可能是最容易概念化的。假装你的2个C ++对象都有一个日志文件。如果没有正确运行的复制构造函数,它们都可以记录到同一个文件。如果在类析构函数中,文件被关闭会发生什么?那么第二个C ++对象将从它下面关闭它的日志文件。一种解决方案是创建一个复制构造函数,为新构造的实例创建一个新文件。因此,当第一个C ++对象消失时,它不会使用第二个C ++对象的日志文件。它们有2个独立的日志文件,它们不能相互干扰。

此示例可以扩展到动态内存。如果没有复制构造函数,您将只有2个指向同一地址的指针。如果一个析构函数删除了那个内存,那么另一个可能没有意识到它所指向的内存已经消失了。同样重要的是,您可能不希望一个对象写入另一个对象的成员:)。