该规则基本上表明,如果一个类定义了以下一个(或多个),则它可能应该显式定义所有三个,即:
- 析构函数
- 复制构造函数
- 复制分配运算符
现在让我们试着理解为什么?
默认的构造函数和赋值运算符执行浅表复制,当我们需要深度复制时(例如,当类包含指向动态分配的资源的指针时),我们将创建自己的构造函数和赋值运算符
首先,析构函数做什么?它包含应该在对象被销毁时运行的代码。只影响对象的内容将是无用的。在销毁过程中的对象不能对其进行任何更改。因此,析构函数会影响整个程序的状态。
现在,假设我们的类没有副本构造函数。然后,复制对象会将其所有数据成员复制到目标对象。这些物体被摧毁时会发生什么?析构函数运行两次。而且,析构函数具有可用于每个被破坏对象的相同信息。总结起来,在没有复制构造函数的情况下,析构函数的执行应该发生一次,而执行两次。这种重复执行是麻烦的根源。
编码示例如下:
// In the below C++ code, we have created
// a destructor, but no copy constructor
// and no copy assignment operator.
class Array
{
private:
int size;
int* vals;
public:
~Array();
Array( int s, int* v );
};
Array::~Array()
{
delete vals;
vals = NULL;
}
Array::Array( int s, int* v )
{
size = s;
vals = new int[ size ];
std::copy( v, v + size, vals );
}
int main()
{
int vals[ 4 ] = { 11, 22, 33, 44 };
Array a1( 4, vals );
// This line causes problems.
Array a2( a1 );
return 0;
}
在上面的示例中,程序超出范围后,将调用类析构函数,而不是调用一次,而是调用两次。首先是由于a1的删除,然后是a2的删除。默认的复制构造函数复制指针vals ,并且实际上不为其分配内存。因此,删除a1时,析构函数将释放vals 。试图被析构函数删除时,包含实例的所有后续val都会导致程序崩溃,因为val不再存在。
复制分配运算符的情况与此类似。如果一个类没有显式定义的赋值运算符,则将所有源数据成员隐式分配给目标的相应数据成员。总而言之,它创建了一个副本,这又是先前定义的相同问题。
参考:
- https://zh.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
- http://www.drdobbs.com/c-made-easier-the-rule-of-three/184401400
要从最佳影片策划和实践问题去学习,检查了C++基础课程为基础,以先进的C++和C++ STL课程基础加上STL。要完成从学习语言到DS Algo等的更多准备工作,请参阅“完整面试准备课程” 。