需要析构函数的类也需要拷贝和赋值操作
当我们决定一个类时候要定义它自己版本的拷贝构造控制成员时,一个基本元组时首先确定这个类是否需要一个析构函数。通常,对析构函数的需求要比对拷贝狗奥挥着复制构造运算符的需求更为明显。如果这个类需要一个析构函数,我们机会可以肯定他也需要一个靠别构造函数和一个拷贝赋值运算符。
class HasPtr{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
~HasPtr(){delete ps;}
//错误:HasPtr需要一个拷贝构造函数和一个拷贝赋值运算符
}
在这个版本的类定义中,构造函数中分配的内存将在HasPtr对象销毁时被自动释放。但不幸的是,我们引入可一个严重的错误!这个版本的类使用了合成的靠别狗奥函数和拷贝赋值运算符,这些函数简单拷贝指针成员,意味着读个HasPtr对象可能指向相同的内存。
HasPtr f(HasPtr hp)//HasPtr是传值参数,所以将被拷贝
{
HasPtr ret=hp; //拷贝给定的HasPtr
//处理ret
return ret//ret和hp被销毁
}
当f返回时,hp和ret都被销毁,在两个对象上都会调用HasPtr的析构函数。此析构函数会delet ret和hp中的指针成员。但这两个对象包含相同的指针,此代码hi导致此指针被delete两次,这显然是一个错误。将发生什么是未定义的。
此外,f的调用者还会使用传递给f的对象
HasPtr p("some values");
f(p);
HasPtr q(p);//现在p和q都指向无效内存
需要拷贝操作的类也需要赋值操作,反之亦然
考虑一个类为每个对象分配一个独有的,唯一的序号。这个类需要一个拷贝构造函数为每个新创建的对象生成一个新的、独一无二的序号。除此之外、这个拷贝构造函数从给定对象拷贝其他数据成员。这个类还需要自定会议拷贝赋值运算以免将序号赋予目的对象。但是,这个类不需要自定义析构函数。
这个例子映出了第二个基本原则:如果一个类需要一个拷贝构造函数,几乎可以确定它也需要一个拷贝赋值构造函数。反之亦然——如果一个类需要一个拷贝赋值运算符,几乎可以肯定它也需要一个拷贝构造函数。然而,无论是需要拷贝构造函数还是需要拷贝赋值运算符都不许意味着也需要析构函数。