Default Constructor

首先我们需要认清一件事情

编译器会为我们的类生成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
9
class Foo {
public:
Foo();
};

class Bar {
private:
Foo foo;
};

那么编译器就会为Bar生成一个Bar::Bar(),并插入对应的代码初始化member class object

1
2
3
4
Bar::Bar() {
//伪代码
foo.Foo::Foo();
}

如果显式定义了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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Widget {
public:
virtual void flip() = 0;
};

class Whistle : public Widget {...};

void callFlip(const Widget& widget) {
widget.flip();
}

void Foo() {
Whistle w;

callFlip(w);
}

此时,widget.flip()的虚拟调用操作会被重写

1
(*widget.vptr[1])(&widget); // 为了调用Whistle::flip(this);

由于vptr指向的是一个vtblwidget.vptr[1]就是对应的函数指针

那么调用函数就变成了(*widget.vptr[1])(&widget) , &widget是传入对应实例(例如w)的this指针

为了达到这种效果,编译器必须为每一个Widget object以及其dervied class的object的vptr指定正确的值用来指向正确的vtbl

这样的话,倘若一个class没有constructor,那么就会合成一个,如果存在,那么就会安插相应的vptr初始化代码

“带有一个Vitrual Base Class”的Class

1
2
3
4
5
6
7
8
class X {public : int i;};
class A : public virtual X {public : int j; };
class B : public virtual X {public : double d; };
class C : public A, public B {public : int k; };

void foo (const A* pa) {
pa->i = 1024;
}

我们知道,在虚拟继承中virtual base class只有一个实例

那么我们必须确定virtual base class在derived class object中的位置

那么,编译器可能进行的改写如下

1
2
3
void 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的默认值