默认操作
当定义一个类时,编译器可能为类的对象定义以下几个默认操作:
- 默认构造函数
- 拷贝操作(拷贝赋值和拷贝初始化)
- 移动操作(移动赋值和移动初始化)
- 析构函数
这些(默认)操作都会递归地对类的每个基类和成员进行操作。
构造是“自底向上”进行的。即先构造基类,再构造成员。
析构是“自顶向下”进行的。即先析构成员,再析构基类。
成员和基类是按它们在声明中出现的顺序构造的,按相反的顺序销毁。
采用这种方式,构造函数和析构函数是否正确工作都依赖于定义良好的基类的成员对象。
eg:
struct D : B1, B2
{
M1 m1;
M2 m2;
};
假定已经定义了B1,B2,M1和M2,则可以编写以下代码:
D f()
{
D d; //默认初始化
D d2 = d; //拷贝初始化
d = D{}; //默认初始化,然后拷贝赋值
return d; //d移出f()
}//d和d2会被销毁
- 在上面的例子中,d的默认初始化操作会依次调用四个默认构造函数:
B1::B1()
B1::B2()
M1::M1()
M2::M2()
如果其中有一个构造函数不存在或者无法调用,则d的构造操作就会失败。
- return时会依次调用四个移动构造函数:
B1::B1()
B1::B2()
M1::M1()
M2::M2()
如果其中有一个不存在或者无法调用,则return会失败。
- d的析构操作会依次调用四个析构函数
M2::~M2()
M1::~M1()
B1::~B2()
B1::~B1()
如果其中有一个析构函数不存在或者无法调用,则d的析构操作就会失败。
- 如果已经为类定义了构造函数,则编译器就不再自动生成隐式构造函数。