Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在什么情况下,我必须为我的C+提供赋值运算符、复制构造函数和析构函数+;上课?_C++_Destructor_Copy Constructor_Assignment Operator - Fatal编程技术网

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

C++ 在什么情况下,我必须为我的C+提供赋值运算符、复制构造函数和析构函数+;上课?,c++,destructor,copy-constructor,assignment-operator,C++,Destructor,Copy Constructor,Assignment Operator,假设我有一个类,其中唯一的数据成员类似于std::string或std::vector。我需要提供复制构造函数、析构函数和赋值运算符吗?如果需要,您需要提供它们。或者您的类的可能用户。析构函数始终是必须的,并且复制构造函数和赋值运算符由编译器自动创建。(至少MSVC)通常的经验法则是:如果你需要其中一个,那么你就需要全部 不过,并不是所有的课程都需要它们。如果您的类不包含任何资源(最显著的是内存),那么没有这些资源您就可以了。例如,具有单个字符串或向量成分的类实际上并不需要它们,除非您需要一些特

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

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

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


不过,并不是所有的课程都需要它们。如果您的类不包含任何资源(最显著的是内存),那么没有这些资源您就可以了。例如,具有单个
字符串
向量
成分的类实际上并不需要它们,除非您需要一些特殊的复制行为(默认值将仅复制到成员上)。

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

这些容器将需要一个“复制可构造”元素,如果您不提供复制构造函数,它将通过从类成员(浅复制)中推断来调用类的默认复制构造函数

关于默认复制构造函数的简单解释如下:


对于析构函数也是如此,如果您不提供析构函数,则容器需要访问您的析构函数或默认类析构函数(即,如果您将析构函数声明为私有,则它将不起作用)

如果您的类只包含向量/字符串对象作为其数据成员,则不需要实现这些。C++ STL类(如vector、string)有自己的复制cTor、重载赋值运算符和析构函数。
但是,如果您的类在构造函数中动态分配内存,那么简单的浅拷贝将导致麻烦。在这种情况下,您必须实现复制构造函数、重载赋值运算符和析构函数。

当您有一个需要深度副本的类时,您应该定义它们

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

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

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

不适用于字符串或向量,因为简单的构造函数/析构函数等就可以了


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

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


根据我的经验,最好是自己定义它们,并养成习惯,确保在更改类时保留它们。首先,您可能希望在调用特定的ctor或dtor时设置断点。不定义它们也可能导致代码膨胀,因为编译器将生成对成员ctor和dtor的内联调用(Scott Meyers对此有一节)

此外,您有时还希望不允许使用默认的复制系数和指定。例如,我有一个应用程序,它存储和处理非常大的数据块。我们通常有一个相当于STL向量的容器来容纳数百万个3D点,如果我们允许这些容器被复制构建,那将是一场灾难。因此,ctor和赋值运算符被声明为私有的,并且没有定义。如果有人写信的话

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


因此,总的来说,有很多理由避免自动生成的编译器函数。

当您需要编写自己的三大编译器函数时,我可以想到一些情况。所有标准容器都知道如何复制和销毁它们自己,所以您不必编写它们。以下是如何知道您何时这样做:

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

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

会有人继承我的班级吗?

基类需要一个析构函数。建议将它们设置为
公共
虚拟
(最常见的情况)或
受保护
和非虚拟,具体取决于您要对它们执行的操作。编译器生成的析构函数是公共的和非虚拟的,因此您必须编写自己的析构函数,即使其中没有任何代码。(注意:这并不意味着您必须编写复制构造函数或赋值运算符。)

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

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

底线:


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

也许最好说:如果班级拥有资源。事实上,
bar
实例
c
所指向的可能在其他地方拥有和控制