[C++]《深度探索C++对象模型》读书笔记 nontrivial default constructor

C++里,如果程序员没有显式的定义默认构造函数(default constructor),编译器会在需要的时候生成一个,也就是隐式地声明出来。

隐式声明的默认构造函数有两种,一种是trivial(无用的) constructor,什么都不做;另一种是nontrival constructor,编译器合成的是后者。

在四种情况下,编译器需要合成nontrival constructor:

1. 带有"Default Constructor"的成员类对象

class A {public: A(), ... };

class B {public: A a;};

int f()
{
    B b;  //此处须调用B的默认构造函数
}

因为B::a是一个member object,且其class A拥有default constructor,所以编译器在需要调用B的默认构造函数时,会为B生成如下的默认构造函数

inline B::b()
{
    // C++伪码
    a.A::A();  // 调用a的默认构造函数
}

但需要注意的是,编译器产生的默认构造函数不会初始化基本类型的成员变量。
如果class B中有int类型的变量,其值不会被默认构造函数初始化为0。

还有一种情况,如果class B已经拥有一个默认构造函数,但其中并未对其对象成员进行初始化,如下例:

class B {public: A a, int num};

B::B() {num = 0}

对于这种情况,编译器会扩张已有的constructor,将对象成员的构造过程安插在user code前,像这样:

B::B()
{
    a.A::A();     // 插入的compiler code
    num = 0;      // user code
}

 另外还需注意的是,编译器所安插的代码会按照“对象在class中的声明顺序”来调用各个constructor,在user code前。

2. "带有Default Constructor"的Base Class

如果一个class派生自一个“带有default constructor”的base class,那个这个class的default constructor需要被编译器合成出来,以调用上一层base class的default constructor。
很多人都了解“子类在调用构造函数时,会先调用父类的构造函数”,这正是因为编译器在我们定义的constructor中进行了上述扩展。

3. "带有Virtual Function"的Class

虚函数表(vtbl)的相关实现机制超出了本文的范围,读者可找相关文章或书藉来学习。

如果class或继承的base class中声明了virtual function(虚函数),编译器会对default constructor进行扩张:
  1. 为class产生一个virtual function table。
  2. 为每个class object创建一个额外的point member(vptr),指向相应的virtual function table。

此外,编译器还会对“虚函数的调用操作”进行修改,如b.f(),会被改为:

(*(b.vptr[1])) (&b);
// 1表示函数f()在vtbl中的索引值,(b.vptr[1])返回了函数f()的地址。
// &b表示交给f()函数调用的this指针,这里涉及到了name magling的知识,读者可自行搜索。

4. "带有一个Virtual Base Class"的Class

虚基类(virtual Base Class)指多继承时,某个base class可能会出现在多个继承路径上。为了防止该基产生多个拷贝,可将基类的继承声明为虚拟的,这样就只会继承基类的一份拷贝。

虽然这样base class只有一份拷贝,但在多态环境下,编译器无法固定”经由某个指针类型而存取的base class中的成员”的实际偏移位置,因为其实际类型是可以改变的。
cfont对这个问题的解决方法是:在derived class object的每一个virtual base class中安插一个指针__vbcX,所有经由引用或指针对virtual base class的操作都可以通过该指针来完成。

而对__vbcX的初始化,正是编译器在default constructor中进行的扩张。

对于这4种情况以外的,且没有声明default constructor的class,我们说它们拥有的是trivial default constructors,实际上它们并没有被合成出来。
所以,“任何class如果没有定义default constructor,都会被生成一个出来”这种说法是错误的。

最后还有一点要注意,生成出来的nontrivial default constructor只对类对象进行初始化,或基本类型(int, double, string)的成员变量还需要程序员来显式的进行初始化。

原文地址:https://www.cnblogs.com/every2003/p/2847907.html