C++对象模型:构造、析构

构造

  1. 无继承
  • Plain Old Data(纯data)

    1
    2
    3
    4
    typedef struct Point{
    double x;
    double y;
    }Point;

    直接逐bit拷贝,编译器甚至没有生成默认构造和析构函数。

  • 抽象数据

1
2
3
4
5
6
7
class Point{
public:
Point(double x = 0.0, double y = 0.0):x(x),y(y){ }
private:
double x;
double y;
};

默认的bitwise copy已经足够。

  • 带有虚函数

在执行构造函数的时候扩充代码,主要是对虚表进行初始化。
包括拷贝构造和拷贝赋值的时候,都会对虚表进行初始化。

  1. 继承

继承情况下会增加大量的隐藏代码:

  • 记录初始化列表初始化操作并按照变量声明顺序进行初始化,这些初始化操作代码会放到构造函数内(但对于member object会少一次拷贝构造)。
  • 对于没有在初始化列表中的成员变量,执行默认构造。
  • 再此之前,先初始化虚表指针。
  • 在此之前,调用基类的构造函数,以声明顺序为准。
    • 如果基类在初始化列表中,把参数固定为初始化列表中的参数。
    • 若没有在初始化列表,调用默认构造。
    • 若为多重继承第二个之后的class,调整this指针。
  • 在此之前,调用虚基类的构造函数,从左到右,从浅到深。
    • 若在初始化列表,传入参数然后构造,若没有在列表,调用默认构造。
    • virtual base class object偏移量在执行器必须可存取。
      具体例子见《深度探索c++对象模型》P207
  1. 虚拟继承
  • 共同父类怎么初始化?

在虚继承的时候,由于有共同父类的原因,对于子类的构造函数中的代码扩充,需要引入判断当前类是否为most_derived,即最后一个继承的子类。
在最后继承子类的构造函数中,对于父类的构造函数传递most_derived = false;从而来抑制父类对于共同父类的构造。

  • 构造函数调用共同虚函数怎么初始化?

如果父类和子类的虚函数存在同名函数,那应该怎么初始化?是调用本体的虚函数还是全部调用继承类的虚函数?
C++规则是在类内调用的话全都应该是本体的虚函数。调用虚函数就需要虚函数表,那么应该怎么初始化?

  • 在base class constructors调用之后,在初始化列表和user code之前。

因此可以这么理解,在虚继承机制下,一个子类首先变成共同父类,然后依次变成父类,最后构造本体,因此它的构造过程是:

  • 在子类的构造函数中,调用共同父类和父类的构造函数。
  • 初始化对象的虚表指针,指向相关的虚表。
  • 在构造函数体内展开初始化列表。
  • 执行user code。
  1. 对象复制

拷贝赋值什么时候会展现不会展现bitwise语义?

  • class内部的某个成员对象有拷贝赋值。
  • 基类有拷贝赋值。
  • 存在虚函数。
  • 存在虚父类。

一旦展现出bitwise语义,编译器就不为我们合成相关的函数。

  1. 析构

析构函数什么时候会被合成?
class内部的member object含有析构函数。
析构函数调用顺序和构造函数相反。