首先我们需要认清一件事情
编译器会为我们的类生成default constructor(默认构造函数)
之类的成员函数
但是关键在于,产生的条件是在需要的时候
那么,default constructor
什么时候才会生成呢?
让我们分情况来讨论
“带有Default Constructor”的 Member Class Object
如果一个class没有任何的构造函数,但是它内含一个member object(成员对象),并且这个对象带有default constructor
那么为了构造这个member class object,编译器必须为这个class合成一个default constructor
例如1
2
3
4
5
6
7
8
9class Foo {
public:
Foo();
};
class Bar {
private:
Foo foo;
};
那么编译器就会为Bar生成一个Bar::Bar()
,并插入对应的代码初始化member class object
1 | Bar::Bar() { |
如果显式定义了constructor,那么编译器就会扩张已存在的所有constructor,在其中安插如上的初始化代码
如果存在多个member class object并且每一个都要求调用default constructor进行初始化操作
那么在安插初始化代码的时候,就会按照member的声明顺序调用其default constructor
“带有Default Constructor”的Base Class
类似的道理,如果某个class派生于一个拥有default constructor的base class(基类)
那么所进行的操作也和上面的一样,会在派生类中生成或者扩张构造函数,安插base class的constructor
“带有一个Virtual Function”的Class
当
class声明或者继承一个virtual function
class派生自一个继承串链,其中有一个或者更多的virtual base class(虚基类)
那么下面两个扩张将会在编译期间发生
编译器会产生一个vtbl(virtual table),存放class的virtual function地址
在每一个class object中,一个指向vtbl的vptr会被合成出来
1 | class Widget { |
此时,widget.flip()的虚拟调用操作会被重写
1 | (*widget.vptr[1])(&widget); // 为了调用Whistle::flip(this); |
由于vptr
指向的是一个vtbl
,widget.vptr[1]
就是对应的函数指针
那么调用函数就变成了(*widget.vptr[1])(&widget)
, &widget
是传入对应实例(例如w)的this指针
为了达到这种效果,编译器必须为每一个Widget object以及其dervied class的object的vptr指定正确的值用来指向正确的vtbl
这样的话,倘若一个class没有constructor,那么就会合成一个,如果存在,那么就会安插相应的vptr初始化代码
“带有一个Vitrual Base Class”的Class
1 | class X {public : int i;}; |
我们知道,在虚拟继承中virtual base class只有一个实例
那么我们必须确定virtual base class在derived class object中的位置
那么,编译器可能进行的改写如下1
2
3void foo (const A* pa) {
pa->_vbcX->i = 1024; //_vbcX是编译器产生的指针,用来指向virtual base class X
}
这样子的话,我们必须保证_vbcX在构造期间完成,这就是合成一个default constructor的理由
总结
以上四种情况会让编译器为未声明constructor的class合成一个default constructor
member class object拥有default constructor
base class拥有default constructor
(继承链中)存在vritual function,需要设置vptr的初始值
存在virtual base class,需要产生一个指向virtual base class的指针
注意下列误区
任何class如果没有定义default constructor,编译器就会合成一个出来
default constructor会显式设定class内每一个data member的默认值