c ++:初始化列表顺序中的构造函数

时间:2017-09-17 17:57:12

标签: c++ initialization

关于以下代码如何运行,我有几个问题

using namespace std;
class A
{
    int i;
public:
    A() { i = 7; cout << 1 << f() << i; }
    A(int i) :i(i) { cout << 1 << f() << i; }
    char f() { return 'A'; }
};
class B : public virtual A
{
    int i;
public:
    B(int i) : A(i), i(++i) { cout << 2 << i; }
    virtual char f() { return 'B'; }
};
class C : public virtual A {
public:
    C(int i) : A(i) { cout << 3 << i; }
    virtual char f() { return 'C'; }
};
class D :public A {
public:
    D(int i) { cout << 4 << i; }
    virtual char f() { return 'D'; }
};
class E : public B, public C, public D {
public:
    E() : B(2), C(3), D(4) { cout << 5; }
    virtual char f() { return 'E'; }
};
int main()
{
    E e;
    return 0;
}

因此输出应为'1A723331A7445'。

  • 首先,我想仔细检查我是否正确的思考以下内容:它首先打印'1A7'而不是'1A2',因为输入A的i未初始化。我能正确理解吗?
  • 其次,我想知道为什么C(3)在打印'1A7'之前打印'33'

1 个答案:

答案 0 :(得分:1)

是的,所以让我先说你已经创建了一个真正混乱的类型层次结构。如果您试图了解初始化顺序,那么这个例子可能会让您感到困惑。

无论如何,为了使事情更清楚,I modified your code并在每个c&#39; tor打印语句的末尾添加斜杠/字符。因此,我们可以更容易地辨别线的哪一部分属于每个c。tor。这给出了以下输出:

  

1A7 / 23/33 / 1A7 / 44/5'/ P>

在我进入初始化顺序之前,您应该知道您指定的所有虚拟函数都是通过动态调度获得的。 c c体中的虚函数将静态绑定。因此,对于我们的意图和目的,您并没有在代码中调用虚拟函数。

现在,引用C ++标准,这就是确定初始化顺序的方式([class.base.init]/13):

  

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

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

  •   
  • 然后,直接基类按声明顺序初始化,因为它们出现在 base-specifier-list 中(无论顺序如何)    mem-initializers )。

  •   
  • 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样无论顺序如何    MEM-初始化)。

  •   
  • 最后,执行构造函数体的复合语句。

  •   

所以让我们分开你的初始化:

1)虚拟A子对象是默认构造的,因为你没有在E()的成员初始化列表中指定它,它为对象执行A()共享BC,并打印1A7/

2)现在调用了B的c&t,以B(int i)执行i = 2。它将B::i设置为3,并且c&#39;身体打印23/

3)通过C调用C(int i)来构建i = 3。这会打印33/

4)现在是时候构建D了。因此,您使用D(int i)致电i = 4。由于D非虚拟地从A继承,因此它将具有需要构建现在的独特A子对象。

  1. 您再次没有在成员初始值设定项列表中为其指定参数,因此A是默认构造的。这会打印1A7/

  2. 现在D(int i)的正文已运行,并打印44/

  3. 5)最后,调用E()的正文,并打印5