三/五法则
什么是三法则
如果需要析构函数,则一定需要拷贝构造函数和拷贝赋值操作符。
什么是五法则
在较新的 C++11 标准中,为了支持移动语义,又增加了移动构造函数和移动赋值运算符,这样共有五个特殊的成员函数,所以又称为“C++五法则”;
如果需要析构函数,则一定需要拷贝构造函数和拷贝赋值操作符。
原因:
类中出现了指针类型的成员。有指针类型的成员,我们必须防止浅拷贝问题,所以,一定需要拷贝构造函数和赋值操作符,这两个函数是防止浅拷贝问题所必须的。
一个对象拥有额外的资源(指针指向的内存),但另一个对象使用合成的拷贝构造函数也同时拥有这块资源。当一方对象被销毁后,析构函数释放了资源,这时另一个对象便失去了这块资源(但程序员还不知道)。
class HasPtr
{
public:
HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0) {}
//HasPtr(const HasPtr& hasPtr);
//HasPtr& operator=(const HasPtr& hasPtr);
~HasPtr() { delete ps; };
private:
std::string* ps;
int i;
};
HasPtr f(HasPtr hp) // 使用了合成拷贝构造函数,使得hp.ps 和 p.ps 指向相同内存
{
HasPtr ret = hp; // 使用了合成拷贝构造函数,使得ret.ps 和 hp.ps 指向相同内存
return ret; // retrurn 之后,ret和hp 会被释放资源,调用析构函数 析构函数会delete ps 导致p.ps 的内存被释放。
}
HasPtr p("some value");
f(p); // 当f结束时,p.ps 指向的内存被释放
HasPtr q(p); // p和q都指向无效内存
如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝运算符
需要拷贝操作的类也需要赋值操作,反之亦然,但不一定意味着需要析构函数
与三之法则不同的是,不提供移动构造函数和移动赋值运算符通常不是错误,但会导致失去优化机会。
2023.6.22 补充说明
如果一个类存在const成员 -> 则不能使用合成的拷贝赋值操作。
如果一个类中存在引用成员 -> 则不能使用合成的拷贝赋值操作,且默认构造函数也是被删除的。|