首页 > 解决方案 > 遵循 C++ 中的构造函数执行顺序

问题描述

我有以下程序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)thenD(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++inheritanceconstructorvirtualmultiple-inheritance

解决方案


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

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

(13.1) — 首先,并且仅对于最派生类 (6.6.2) 的构造函数,虚拟基类按照它们在基的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化classes,其中“从左到右”是派生类 base-specifier-list 中基类的出现顺序。

对于此类声明

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);

深度优先构造函数是 virtual public B 的默认构造函数。

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

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

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

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

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

(13.2) — 然后,直接基类按照它们出现在 base-specifier-list 中的声明顺序进行初始化(无论 mem-initializers 的顺序如何)。

C::C()

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

E::E(int)

被输出。

考虑到虚拟继承类的构造函数首先被调用,并且只被调用一次。所以类 C 的构造函数不会调用类 B 的构造函数,因为在调用 C 的构造函数之前已经调用了对应的构造函数。


推荐阅读