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

时间:2010-03-05 11:10:55

标签: c++ destructor copy-constructor assignment-operator

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

9 个答案:

答案 0 :(得分:13)

如果您的类仅包含矢量/字符串对象作为其数据成员,则无需实现这些。 C ++ STL类(如vector,string)有自己的copy ctor,重载赋值运算符和析构函数。

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

答案 1 :(得分:5)

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

但并非所有课程都需要它们。如果你的班级没有资源(记忆,最值得注意的是),没有它们你就没事了。例如,具有单个stringvector成分的类并不真正需要它们 - 除非您需要一些特殊的复制行为(默认只会复制成员)。

答案 2 :(得分:4)

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

答案 3 :(得分:3)

当你需要写自己的三巨头时,我可以想到几个案例。所有标准容器都知道如何复制和销毁自己,因此您不一定需要编写它们。以下是知道你何时做的事情:

  

我的班级是否拥有任何资源?

指针的默认复制语义是复制指针的,而不是它指向的位置。如果你需要深层复制某些东西,即使它存储在标准容器中,你需要编写自己的复制构造函数和赋值运算符。您还需要编写自己的析构函数以正确释放这些资源。

  

有人可以从我的班级继承吗?

基类需要析构函数。 Herb Sutter建议publicvirtual(最常见的情况)或protected和非虚拟,具体取决于您要对它们执行的操作。编译器生成的析构函数是公共的和非虚拟的,所以你必须编写自己的析构函数,即使它没有任何代码。 (注意:这并不意味着您必须编写复制构造函数或赋值运算符。

  

我应该阻止用户复制我班级的对象吗?

如果您不希望用户复制您的对象(可能太贵),您需要声明复制构造函数和赋值运算符protected或{{1} }。除非您需要,否则不必实现它们。 (注意:这并不意味着你必须编写析构函数。

底线:

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

答案 4 :(得分:2)

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

根据我的经验,最好自己定义它们,并养成在改变课程时确保维护它们的习惯。首先,您可能希望在调用特定的ctor或dtor时设置断点。同样没有定义它们会导致代码膨胀,因为编译器将生成对成员ctor和dtor的内联调用(Scott Meyers有一个关于此的部分)。

此外,您有时也想禁止默认复制和分配。例如,我有一个存储和操作非常大的数据块的应用程序。我们通常拥有相当于容纳数百万个3D点的STL向量,如果我们允许这些容器被复制构造,那将是一场灾难。因此,ctor和赋值运算符被声明为私有而未定义。这样,如果有人写

class myClass {
  void doSomething(const bigDataContainer data); // not should be passed by reference
}
然后他们会得到一个编译器错误。我们的经验是,显式的()或clone()方法不易出错。

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

答案 5 :(得分:1)

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

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

对于析构函数来说,如果你没有提供析构函数,那么容器需要访问你的析构函数或你的默认类析构函数(例如,如果你将析构函数声明为私有,它将不起作用)

答案 6 :(得分:0)

如果需要,您需要提供它们。或者您的课程的可能用户。析构函数始终是必须,并且编译器会自动创建复制构造函数和赋值运算符。 (至少MSVC)

答案 7 :(得分:0)

当你有一个需要深拷贝的类时,你应该定义它们。

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

class foo {
private:
    int a,b;
    bar *c;
}

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

答案 8 :(得分:0)

不是字符串或向量,因为琐碎的构造函数/析构函数等都可以。

如果您的类具有指向其他数据的指针并需要深层副本,或者您的类拥有必须取消分配或必须以特殊方式复制的资源。