构造函数的调用顺序

Call order of constructors

本文关键字:顺序 调用 构造函数      更新时间:2023-10-16
#include <iostream>
using namespace std;    
struct A{
A() {cout << "A" << endl;}
A(int a) {cout << "A+" << endl;}
};
struct B : virtual A{
B() : A(1) {cout << "B" << endl;}
};
struct C : virtual A{
C() : A(1) {cout << "C" << endl;}
};
struct D : virtual A{
D() : A() {cout << "D" << endl;}
};
struct E : B, virtual C, D{
E(){cout << "E" << endl;}
};
struct F : D, virtual C{
F(){cout << "F" << endl;}
};
struct G : E, F{
G() {cout << "G" << endl;}
};
int main(){
G g;
return 0;
}

程序打印:

A
C
B
D
E
D
F
G

我想知道应该使用什么规则来确定调用构造函数的顺序。谢谢

您应该遵循C++标准中给出的规则:

[C++11: 12.6.2/10]:在非委托构造函数中,初始化按以下顺序进行:

  • 首先,仅对于最派生类(1.8)的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先左到右遍历中出现的顺序进行初始化,其中"从左到右"是派生类基说明符列表中基类的出现顺序
  • 然后,直接基类按照它们在基说明符列表中出现的声明顺序进行初始化(无论mem初始化器的顺序如何)
  • 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样,与mem初始化器的顺序无关)
  • 最后,执行构造函数主体的复合语句

[注意:声明顺序是为了确保基本子对象和成员子对象以与初始化相反的顺序销毁。--结束注释]

虚拟基子对象首先由最派生的类构造,然后再构造任何其他基。这是唯一有意义的方法,因为在运行时构建对象之前,虚拟基与大多数派生对象的关系是未知的(因此是"虚拟的")。忽略虚拟基的所有中间初始值设定项。

那么,你的虚拟基地是什么?CCD_ 2由CCD_ 3和CCD_。E实际上是从C派生而来的,而CCD_6又实际上是从CCD 7派生而来,因此AC是第一位的。接下来,F不添加任何进一步的虚拟基地。接下来,E具有非虚拟碱基BD,按顺序,它们被构造成下一个,然后E是完整的。然后是F的非虚拟基D,并且F是完整的。最后,G是完整的。

总之,它是虚拟碱基AG0,然后是非虚拟碱基BDEDF,然后是G本身。

您可以从C++标准的这句话中调查构造函数调用的顺序,并尝试自己捕获

10在非委托构造函数中,初始化在以下顺序:--首先,并且仅适用于构造函数的派生类(1.8),虚拟基类按以下顺序初始化它们出现在有向的从左到右的深度优先遍历中基类的非循环图,其中"从左到右"是基类在派生类中的外观基说明符列表。--然后,直接基类在中初始化在基说明符列表中出现的声明顺序(不管mem初始化程序的顺序如何)。--然后,非静态数据成员按照在中声明的顺序进行初始化类定义(同样与mem初始化程序)。--最后构造函数主体被执行。【注:申报顺序为被授权确保基本子对象和成员子对象在初始化的相反顺序--尾注]