在C++2.0之前存在Big Three的说法,即类中的三大函数:析构函数、拷贝构造函数、拷贝赋值函数。
C++2.0之后添加了移动(&&)语义,扩展至Big Five,即类的五大函数:析构函数、拷贝构造函数、移动构造函数、拷贝赋值函数、移动赋值函数。
(疯狂吐槽一下,Big Three和Big Five中为什么没有构造函数。)
对于一般的类,构造函数与Big Three在用户没有手动定义的前提下,编译器会生成默认版本,且假如类中没有指针类的数据成员,默认版本一般已经够用,不需要用户额外定义。若用户已经有了自定义版本,编译器则不会生成默认版本。
特别是对于构造函数,若针对单一类而言,默认版本貌似没有什么意义,但是假如该类有继承父类,那么编译器会在默认构造函数中自动添加调用父类构造函数的的代码。
=delete与=default则正是用来修饰Big Five与构造函数的。=delete用来告诉编译器,即使用户没有自定义版本,编译器也不要自作聪明去生成默认版本;后者用来告诉编译器,即使用户有了自定义版本,编译器也同样会去生成一个默认版本。这样来看=delete与=delete是一对完全相反的关键字。
=delete在用法上和作用效果上与虚函数=0有异曲同工之妙。
class Base{
public:
Base(int x){}//1 用户自定义构造函数
Base()=default;//2 在1、2共存的情况下,=default可以修饰版本2的构造函数,告诉编译器生成默认版本的构造函数。此时两个构造函数并存,因为同一个类中,构造函数可以重载
//Base()=delete;//3 若没有版本1、2构造函数,可以用=delete修饰版本3构造函数,告诉编译器不要生成默认版本的构造函数
Base(const Base& b) {}//4 用户自定义拷贝构造函数
//Base(const Base& b)=default;//5 告诉编译器生成默认版本的拷贝构造函数(若已存在版本4或6的拷贝构造函数,那么再定义版本5编译器会报错,因为在一个类中拷贝构造函数不能重载)
//Base(const Base& b)=delete;//6 告诉编译器不要生成默认版本的拷贝构造函数(若已存在版本4或5的拷贝构造函数,那么再定义版本6编译器会报错,因为在一个类中拷贝构造函数不能重载)
Base& operator=(const Base& b){}//7 用户自定义赋值运算符重载
//Base& operator=(const Base& b)=default;//8 告诉编译器生成默认版本的拷贝赋值函数(若已存在版本7或9的拷贝赋值函数,那么再定义版本8编译器会报错,因为在一个类中拷贝赋值函数不能重载)
//Base& operator=(const Base& b)=delete;//9 告诉编译器不要生成默认版本的拷贝赋值函数(若已存在版本7或8的拷贝赋值函数,那么再定义版本9编译器会报错,因为在一个类中拷贝赋值函数不能重载)
Base(const Base&& b) {}//10 用户自定义移动构造函数
//Base(const Base& b)=default;//11 编译报错=》“Default::Default(const Default &&)”: 不是可默认为的特殊成员函数。即移动构造函数不可被=default修饰
//Base(const Base& b)=delete;//12 移动构造函数可以被=delete修饰,但没什么意义,只是编译器不报错
//void fun1()=default;//对于其他函数使用=default修饰毫无意义,而且编译也会报错
void fun2()=delete;//对于其他函数使用=delete修饰也没什么太大意义,但是编译并不会报错
//~Base()=delete;//若析构函数用=delete修饰,则后面实例化对象时会出问题——>即析构函数不能用delete修饰
//若实例化方式为Base b; 则直接编译报错=》使用已删除的函数Base::~Base()
//若实例化方式为Base* b=new Base; 则不会直接报错,只有delete b;时才会编译报错=》使用已删除的函数Base::~Base()
~Base()=defaut;//告诉编译器生成默认版本的析构函数
};