018 三/五法则
2020-03-22 本文已影响0人
赵者也
需要析构函数的类也需要拷贝和赋值操作
class HasPtr
{
public:
HasPtr(const std::string &s = std::string())
: ps(new std::string(s))
, i(0)
{}
~HasPtr() { delete ps; }
// ...
};
此时,意味着我们在使用合成的拷贝构造函数和拷贝赋值运算符。这些函数简单拷贝指针成员,这意味着多个 HasPtr 对象可能指向相同的内存:
HasPtr f(HasPtr hp) {
HasPtr ret = hp;
return ret;
}
当 f 返回时,hp 和 ret 都被销毁,在两个对象上都会调用 HasPtr 的析构函数。此析构函数会 delete ret 和 hp 中的指针成员。但这两个对象包含相同的指针值。此代码会导致此指针被 delete 两次,者显然是一个错误。将要发生什么是未定义的。
此外,f 的调用者还会使用传递给 f 的对象:
HasPtr p("some values");
f(p); // 当 f 结束时,p.ps 指向的内存被释放
HasPtr q(p); // 现在 p 和 q 都指向无效内存
p(以及 q)指向的内存不再有效,在 hp(或 ret!)销毁时它就被归还给系统了。
综上所述,如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。
需要拷贝操作的类也需要赋值操作,反之亦然
假设一个类为每个对象分配一个独有的、唯一的序号。这个类需要一个拷贝构造函数为每个新创建的对象生成一个新的、独一无二的序号。除此之外,这个拷贝构造函数从给定对象拷贝所有其他数据成员。这个类还需要自定义拷贝赋值运算符来避免将序号赋予目的对象。但是,这个类不需要自定义析构函数。
这个例子说明:如果一个类需要一个拷贝构造函数,几乎可以肯定它也需要一个拷贝赋值运算符。反之亦然——如果一个类需要一个拷贝赋值运算符,几乎可以肯定它也需要一个拷贝构造函数。然而,无论是需要拷贝构造函数还是需要拷贝赋值运算符都不一定需要析构函数。