C ++继承和初始化顺序

时间:2015-02-15 17:56:16

标签: c++ inheritance

我正在为期末考试而学习。我偶然发现了前几年的这个问题,我似乎无法完全理解发生了什么。 根据此代码,确定输出

#include <iostream>
using namespace std;
struct A {
    A(int a): _a(a) {cout << "A::A, a=" << _a << endl;} 
    ~A() { cout << "A::~" << endl; }
    int _a;
};

struct B: public A
{
    B(int b):A(b) { cout << "B::B" << endl; }
    ~B() { cout << "B::~" << endl; } 
};

struct C: public B
{
    A a;
    B b;
    C(int a=10, int b=20):a(a), b(a*b), B(b) {} 
    ~C() { cout << "C::~" << endl; }
};

int main() {
    C allTogetherNow;
    return 0; 
}

我尝试编译代码,然后收到警告:

  

警告:字段'b'将在基数'B'后初始化         [-Wreorder] C(int a = 10,int b = 20):a(a),b(a * b),B(b){} ~C(){cout&lt;&lt; “C ::〜”&lt;&lt; ENDL; }                               生成^ 1警告。

以及以下输出:

A::A, a=20
B::B
A::A, a=10
A::A, a=200
B::B
C::~
B::~
A::~
A::~
B::~
A::~

破坏秩序有点清晰(最后构造 - 首先被破坏),但我似乎无法看到施工顺序/模式。我错过了什么?我收到的警告的说明会更有帮助。另外,如果您可以向我推荐有关此特定主题的额外阅读材料。

谢谢。

2 个答案:

答案 0 :(得分:3)

这一行:

C(int a=10, int b=20):a(a), b(a*b), B(b) {} 

应该是:

C(int a=10, int b=20): B(b), a(a), b(a*b) {} 

在C ++中,初始化顺序是固定的。对于您的类型C,它始终为:

  1. 基类B
  2. 成员变量A a;
  3. 成员变量B b;
  4. 在初始化列表中,您对其进行了不同的排序。因此,如果您希望初始化程序列表的顺序无关紧要,您的编译器会发出警告。

答案 1 :(得分:2)

初始化顺序在标准中精确定义:

  

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

     

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

     

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

     

- 然后,非静态数据成员按其顺序初始化   在类定义中声明(再次无论顺序如何)   记忆初始化者)。

     

- 最后,复合声明了   构造函数体被执行。

因此,当你初始化C时,它的基类首先被初始化,即B默认为20,它本身需要A首先用20初始化。只有这样才能初始化B完成。

然后,AB被初始化,C的成员被初始化,首先使用默认参数10 a开始,然后使用b(200) }。由于bB,它首先需要初始化自己的基础A。然后可以完成b的初始化。最后,C的初始化完成。

顺便说一句,它不是问题的一部分,但请记住你的考试:

  

12.4 / 7:基础和成员按照构造函数完成的相反顺序销毁。

有关警告的说明:

我的编译器没有生成此警告。没有理由,因为mem-initilizer B(b)明确使用了你的consturctor的参数b。它只是一个假设,但我怀疑你的编译器会产生误报,因为b也是一个成员的名字(在调用base时确实不会初始化)。如果我没错,以下更改不应再发出警告:

 C(int a=10, int bbb=20):a(a), b(a*bbb), B(bbb) { cout << "C::C" << endl;}