【问题标题】:Following the constructor execution order in C++遵循 C++ 中的构造函数执行顺序
【发布时间】:2019-11-13 03:11:33
【问题描述】:

我在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)

【问题讨论】:

  • 请包括你在运行程序时得到的实际输出,以及你认为应该得到的输出。我还建议您使用调试器来单步执行代码。
  • @Someprogrammerdude 我已经添加了预期的输出以及我认为应该如何。我尝试使用调试器(这就是我看到它进入C() 的方式)。
  • 如果标准对您来说太枯燥,请阅读 cppreference.com
  • 我得到了预期的输出。我不确定你为什么从你的角度得到输出——尤其是为什么 C 没有被构造。
  • @Eljay 我认为“预期”意味着“实际发生的事情”,“从我的角度来看”意味着“我所期望的”。 (也就是说,几乎与您的预期相反。)

标签: c++ inheritance constructor virtual multiple-inheritance


【解决方案1】:

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

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

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

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

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

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

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

(13.2) — 然后,直接基类在声明中初始化 它们出现在基本说明符列表中的顺序(无论 mem-initializers 的顺序)。

C::C()

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

E::E(int)

被输出。

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

【讨论】:

  • 谢谢。所以步骤是: 1. 获取具有最多儿子的类并运行其默认构造函数。 2.检查被调用构造函数的签名,如果它有E(x) : D(x)语法调用D(x)。 3. 查看E 扩展它们的所有非虚拟类,并按顺序运行它们的默认构造函数only。这是正确的吗? (请确认调用了唯一的默认构造函数,否则为什么调用 C() 而不是调用 B()
  • 我只是想把它们总结一下,以检查我是否理解正确。因为例如你没有说为什么当我们调用C()时没有调用B()
猜你喜欢
  • 2010-12-25
  • 2016-04-20
  • 2012-04-10
  • 2011-05-01
  • 2013-10-24
  • 1970-01-01
  • 2012-08-22
  • 2016-03-18
  • 1970-01-01
相关资源
最近更新 更多