遵循 C++ 中的构造函数执行顺序

Following the constructor execution order in C++

本文关键字:执行 顺序 构造函数 C++ 遵循      更新时间:2023-10-16

我在C++有以下程序:

class A {
public :
A(){
cout << "A::A()" << endl;
}
A( int x){
cout << "A::A(int)" << endl;
}
};
class B : public A {
public :
B(){
cout << "B::B()" << endl;
}
B( int x){
cout << "B::B(int)" << endl;
}
};
class C : public virtual B {
public :
C(){
cout << "C::C()" << endl;
}
C( int x){
cout << "C::C(int)" << endl;
}
};
class D : public B {
public :
D(){
cout << "D::D()" << endl;
}
D( int x) : B(x){
cout << "D::D(int)" << endl;
}
};

class E : public C, public virtual
D, public virtual B {
public :
E(){
cout << "E::E()" << endl;
}
E( int x) : D(x){
cout << "E::E(int)" << endl;
}
};

int main() {
E(5);
return 0;
}

我试图了解将要打印的内容。我会试着解释我是如何看待这种情况的。起初我们称E(5).在E(int)构造函数中,我们有以下语法:E(int x) : D(x)那么应该调用D(x)构造函数(而不是继承类的构造函数class E : public C, public virtual D, public virtual B,这意味着只应该调用D(x)而不调用C(),D(),B())。在D类中,我们需要称为B().这部分我不明白 - 我们称D(int)构造函数也具有语法:D(int x) : B(x)所以应该调用B(x)而不是B()。如果我们需要使用B()构造函数,即使调用的构造函数B(x)比为什么当我们调用E(5)构造函数时,我们没有执行E().

现在,我接受应该首先调用B()。然后我们调用A()构造函数并执行它,然后B().当我们到达D(x)时,我们打电话给B(x)然后A()所以直到现在我们打印:

A::A()
B::B()
A::()
B::B(int)
D::D(int)

现在我们需要回到E(5)并执行它,但由于某种原因,调用了C()构造函数,我不明白为什么。我们说过,如果我们有E(int x) : D(x)语法,那么我们只执行D(x).这些问题要遵循哪些规则/算法?

编辑: 预期输出:

A::A()
B::B()
A::A()
B::B(int)
D::D(int)
C::C()
E::E(int)

从我的角度来看,输出:

A::A() // not fully agree
B::B() // not fully agree
A::A()
B::B(int)
D::D(int)
E::E(int)

根据C++标准(C++17,15.6.2初始化基和成员,第13页)

13 在非委托构造函数中,初始化在 以下顺序:

(13.1) — 首先,并且仅适用于派生最多的类的构造函数 (6.6.2),虚拟基类按其出现的顺序进行初始化 在有向无环的深度优先从左到右遍历上 基类图,其中"从左到右"是 派生类中基类的外观 基本说明符列表。

对于此类声明

class E : public C, public virtual
D, public virtual B {
public :
E(){
cout << "E::E()" << endl;
}
E( int x) : D(x){
cout << "E::E(int)" << endl;
}
};

和这个显式函数转换表达式

E(5);

深度优先构造函数是虚拟公共 B 的默认构造函数。

所以首先调用class B的默认构造函数

A::A()
B::B()

然后调用转换构造函数D( int )

A::A()
B::B(int)
D::D(int)

最后,非虚拟基class C的构造函数被调用

(13.2) — 然后,直接基类在声明中初始化 它们在基本说明符列表中出现的顺序(无论 内存初始值设定项的顺序)。

C::C()

然后将控件(初始化非静态数据成员后)传递给constructor E正文和消息

E::E(int)

被输出。

考虑到虚拟继承类的构造函数首先且仅调用一次。因此,类 C 的构造函数不会调用类 B 的构造函数,因为在调用 C 的构造函数之前已经调用了相应的构造函数。