我必须在什么情况下为我的C ++类提供赋值运算符,复制构造函数和析构函数?

Under what circumstances must I provide, assignment operator, copy constructor and destructor for my C++ class?

本问题已经有最佳答案,请猛点这里访问。

假设我有一个类,其中唯一的数据成员类似于std::stringstd::vector。我需要提供一个复制构造函数、析构函数和赋值运算符吗?


如果类只包含向量/字符串对象作为其数据成员,则不需要实现这些对象。C++ STL类(如vector、string)有自己的复制cTor、重载赋值运算符和析构函数。

但是,如果您的类在构造函数中动态地分配内存,那么一个简单的浅拷贝将导致麻烦。在这种情况下,您必须实现复制ctor、重载的赋值运算符和析构函数。


通常的经验法则是:如果你需要它们中的一个,那么你就需要它们全部。

不过,并非所有的课程都需要它们。如果你的类没有任何资源(特别是内存),没有它们你会很好的。例如,具有单个stringvector成分的类并不真正需要它们,除非您需要一些特殊的复制行为(默认情况下只复制成员)。


如果向量是由值声明的,则默认的复制构造函数将复制该向量。如果您将指针存储在向量中,那么要小心,在这种情况下,您需要为复制/分配/销毁提供特定的行为,以避免内存泄漏或多次删除。


当你需要写自己的三大时,我可以考虑一些情况。所有标准容器都知道如何复制和销毁它们自己,因此您不必编写它们。以下是如何知道何时进行的操作:

Does my class own any resources?

指针的默认复制语义是复制指针的值,而不是它指向的值。如果需要深度复制某些内容,即使它存储在标准容器中,也需要编写自己的复制构造函数和赋值运算符。您还需要编写自己的析构函数来适当地释放这些资源。

Might someone inherit from my class?

基类需要一个析构函数。Herb Sutter建议根据您想用它们做什么,将它们制作成publicvirtual(最常见的情况)或protected和非虚拟的。编译器生成的析构函数是公共的和非虚拟的,因此即使它没有任何代码,也必须自己编写。(注意:这并不意味着您必须编写复制构造函数或赋值运算符。)

Should I prevent a user from copying objects of my class?

如果您不希望用户复制您的对象(可能这太贵了),您需要声明复制构造函数和赋值操作符protectedprivate。除非你需要它们,否则你不必实现它们。(注意:这并不意味着你必须写一个析构函数。)

底线:

最重要的是理解编译器生成的复制构造函数、赋值运算符和析构函数将要做什么。你不需要害怕他们,但你需要考虑他们,并决定他们的行为是否适合你的班级。


不,但是有很多原因不应该允许编译器自动生成这些函数。

根据我的经验,最好是自己定义它们,并养成习惯,确保它们在你改变班级时保持不变。首先,当调用特定的ctor或dtor时,您很可能希望在上放置一个断点。另外,如果不定义它们,可能会导致代码膨胀,因为编译器将生成对成员ctor和dtor的内联调用(ScottMeyers对此有一节)。

此外,有时您还希望禁用默认的复制目录和分配。例如,我有一个应用程序可以存储和操作非常大的数据块。我们通常拥有一个STL向量,它可以容纳数百万个3D点,如果我们允许复制构建这些容器,那将是一场灾难。因此,ctor和赋值操作符被声明为私有的,并且没有被定义。如果有人写的话

1
2
3
class myClass {
  void doSomething(const bigDataContainer data); // not should be passed by reference
}

然后他们会得到一个编译器错误。我们的经验是显式become()或clone()方法不太容易出错。

所以总而言之,有很多理由避免自动生成编译器函数。


这些容器需要一个"copy-constructable"元素,如果不提供copy构造函数,它将通过从类成员中推断(浅拷贝)来调用类的默认copy构造函数。

关于默认复制构造函数的简单解释如下:http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html

对于析构函数,如果不提供析构函数或默认类析构函数,则容器需要具有对该析构函数或默认类析构函数的访问权(即,如果将析构函数声明为private,则它将不起作用)。


不适用于字符串或向量,因为一般的构造器/析构函数等可以做得很好。

如果您的类有指向其他数据的指针,并且需要深度复制,或者您的类拥有必须解除分配或必须以特殊方式复制的资源。


当您有一个类需要深度复制时,您应该定义它们。

具体来说,任何包含指针或引用的类都应该包含这些指针或引用,例如:

1
2
3
4
5
class foo {
private:
    int a,b;
    bar *c;
}

从主观上讲,我会说总是定义它们,因为编译器生成的版本提供的默认行为可能不是您期望/想要的。


如果需要的话,你需要提供。或者你班的潜在用户。析构函数始终是必需的,复制构造函数和赋值运算符由编译器自动创建。(至少MSVC)