内容摘要
三五法则主要内容
在一个类中,编译器可以为我们合成的函数有默认构造函数,拷贝构造函数,拷贝赋值构造函数,移动构造函数,移动赋值构造函数。这意味着我们有时候可以不用自己去实现这些内容。但是,在默认合成的函数无法满足我们的需求时,我们就要手动合成之。大多数情况下,我们除了要自己实现需要的函数外,还必须实现部分其他的函数,以确保我们的函数能够正确的工作。三五法则就是用来为这样的类内部函数成员的涉及提供指导的。其具体内容可概括为:
- 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数;
- 需要拷贝构造函数,则同样需要拷贝赋值构造函数;
- 析构函数不能是删除的;
- 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的;
- 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作。
需要析构函数的类也需要拷贝构造函数和拷贝赋值函数
若一个类需要析构函数,通常说明合成的构造函数无法释放所管理的资源,比如需要手动释放管理的内存。
需要拷贝构造函数,则同样需要拷贝赋值构造函数;
需要拷贝操作代表这个类在拷贝时需要进行一些额外的操作。 赋值操作 <<< = >>> 先析构+拷贝,所以拷贝需要的赋值也需要。反之亦然。
析构函数不能是删除的
如果类的析构函数是删除的,那么成员便无法销毁。所以在程序中不能定义这个类的对象。可以动态分配该对象并获得其指针,但无法销毁这个动态分配的对象(delete 失效)。
#include <iostream>
#include <string>
typedef unsigned int uint;
using namespace std;
class Person{
public:
Person(uint myage=18, string myname="hhh")
{
age=myage;
name=new string(myname);
}
uint age;
string* name;
~Person()=delete;
};
int main(int argc, char** argv){
Person a;//析构函数是删除的,不能够定义其对象
delete a;
return 0;
}
报错输出:
[Error] use of deleted function ‘Person::~Person()’
#include <iostream>
#include <string>
typedef unsigned int uint;
using namespace std;
class Person{
public:
Person(uint myage=18, string myname="hhh")
{
age=myage;
name=new string(myname);
}
uint age;
string* name;
~Person()=delete;
};
int main(int argc, char** argv){
Person* a=new Person();//这里可以编译通过
delete a;//这里报错
return 0;
}
报错输出:
[Error] use of deleted function ‘Person::~Person()’
如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的
如果没有这条规则,可能会创造出无法被删除的对象。 理论上来说,当析构函数不能被访问时,任何静态定义的对象都不能通过编译器的编译,所以这种情况只会出现在与动态分配有关的拷贝/默认构造函数身上。
如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作
原因很简单,const或引用成员只能在初始化时被赋值一次,而合成的拷贝赋值操作会对所有成员都进行赋值。显然,它不能赋值const和引用成员,所以合成的拷贝构造函数不能被使用,即会被定义为删除的。
本质上,当不可能拷贝、赋值、或销毁类的所有成员时,类的合成拷贝控制函数就被定义成删除的了。
【参考】
C++三五法则
这个总结大部分都是从这个参考里面摘录而来的,感觉原文档记录更加详细,推荐阅读原文档。