我正在准备期末考试。我从前几年偶然发现了这个问题,但我似乎无法完全理解发生了什么。 给定此代码,确定输出
#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;
}
我尝试编译代码,但收到警告:
warning: field 'b' will be initialized after base 'B' [-Wreorder] C(int a=10, int b=20):a(a), b(a*b), B(b) {} ~C() { cout << "C::~" << endl; } ^ 1 warning generated.
和以下输出:
A::A, a=20
B::B
A::A, a=10
A::A, a=200
B::B
C::~
B::~
A::~
A::~
B::~
A::~
销毁顺序有点清楚(最后构造 - 首先销毁),但我似乎无法理解构造顺序/模式。我错过了什么?对我收到的警告 进行澄清会非常有帮助。另外,如果您可以向我推荐有关该特定主题的额外阅读 Material 。
谢谢。
最佳答案
初始化顺序在标准中有明确定义:
12.6.2./10: In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
— Finally, the compound-statement of the constructor body is executed.
所以当你初始化 C 时,它的基类首先被初始化,即默认为 20 的 B
本身需要先用 20 初始化 A
。然后才是初始化B
已完成。
然后,A
和B
被初始化,C
的成员被初始化,首先从a
开始使用默认参数 10,然后使用 b(200)
。由于b
是一个B
,它首先需要初始化它自己的基A
。这样就可以完成b
的初始化了。最后完成C
的初始化。
顺便说一句,这不是问题的一部分,但请记住考试:
12.4/7: Bases and members are destroyed in the reverse order of the completion of their constructor.
警告注意事项:
我的编译器不会生成此警告。没有理由这样做,因为 mem-initilizer B(b)
显然使用了构造函数的参数 b
。这只是一个假设,但我怀疑您的编译器会产生误报,因为 b
也是成员的名称(调用基时确实不会初始化)。如果我是对的,以下更改不应再引发警告:
C(int a=10, int bbb=20):a(a), b(a*bbb), B(bbb) { cout << "C::C" << endl;}
关于C++ 继承和初始化顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28529185/