//This program is taken from http://www.learncpp.com/cpp-tutorial/114-constructors-and-initialization-of-derived-classes/
#include <iostream>
using namespace std;
class A
{
public:
A(int nValue)
{
cout << "A: " << nValue << endl;
}
};
class B: public A
{
public:
B(int nValue, double dValue)
: A(nValue)
{
cout << "B: " << dValue << endl;
}
};
int main()
{
B bClass(5, 4.3);
return 0;
}
当我运行时:
$g++ inherit.cpp -o inherit
$ ./inherit
A: 5
B: 4.3
这是预料之中的。
但是当我改变class B
时:
class B: public A
{
int something;
public:
B(int nValue, double dValue)
: something(nValue), A(nValue)
{
cout << "B: " << dValue << endl;
cout << "B.something: " << something << endl;
}
};
输出是:
$ ./inherit
A: 5
B: 4.3
B.something: 5
当编译器通过B的初始化列表时,它是否仅搜索基类构造函数并跳转到它而不执行其他语句(something(nValue)
)
或者它是否执行所有语句,直到它找到BaseClass构造函数并且只是在Base Class构造函数执行完之后才执行构造函数体?
换句话说,B.something
的值在第一次遇到nValue
语句时会设置为something(nValue)
,或者在A(nValue)之前保持未初始化叫?
答案 0 :(得分:9)
类实例的所有子对象和非静态成员对象都按固定顺序初始化,即按其声明的顺序初始化(即:首先按顺序排列所有子对象(例如A
,{{ 1}},B
用于C
),然后是非静态成员对象。)
在初始化列表中列出初始值设定项的顺序不会影响此行为;但是,您应该始终以正确的顺序编写IL,以避免出现意外,并使代码尽可能易于阅读。
通常应该避免让一个对象初始化程序依赖于另一个对象,但有时可能是必要的。如果可能,只需重用构造函数参数。例如:
class Foo : A, B, C
现在假设我们改为struct Foo {
int a;
int b;
Foo(int a_, int b_) : a(a_), b(a) { } // not so good, depends on order!
Foo(int a_, int b_) : a(a_), b(a_) { } // better, always works
};
,您可能想要将struct Foo : Bar
的初始值设定为Foo
。那将是一场灾难。按顺序编写IL会使这变得不可能:a(a_), b(a), Bar(a, b)
。
答案 1 :(得分:3)
子对象构造(和销毁)的顺序仅由声明基类和成员的顺序决定。初始化程序列表中的顺序将被忽略。在初始化列表中使用不同的子对象顺序必然会使您和代码的其他读者感到困惑:不要这样做。一些编译器会对此发出警告。 Scott Meyer的“Effective C ++”中还有一个项目(强烈推荐阅读;我认为它是任何专业C ++程序员必读的。)
答案 2 :(得分:0)
我在inherit
中运行了gdb
程序,事实证明,在A(nValue)完成执行之前,某些内容仍然未初始化。
Breakpoint 1, main () at inherit.cpp:27
27 B bClass(5, 4.3);
(gdb) nexti
0x08048693 27 B bClass(5, 4.3);
(gdb) s
B::B (this=0xbffff2dc, nValue=5, dValue=4.2999999999999998) at inherit.cpp:18
18 : something(nValue), A(nValue)
(gdb) p something
$2 = 3751924
(gdb) nexti
0x08048760 18 : something(nValue), A(nValue)
(gdb) p something
$3 = 3751924
(gdb) nexti
0x08048764 18 : something(nValue), A(nValue)
(gdb) p something
$4 = 3751924
(gdb) nexti
0x08048767 18 : something(nValue), A(nValue)
(gdb) p something
$5 = 3751924
(gdb) nexti
0x0804876a 18 : something(nValue), A(nValue)
(gdb) p something
$6 = 3751924
(gdb) nexti
A: 5 //A(nValue) HAS FINISHED EXECUTION AND something is still uninitialized
0x0804876f 18 : something(nValue), A(nValue)
(gdb) p something
$7 = 3751924
(gdb) nexti
0x08048772 18 : something(nValue), A(nValue)
(gdb) p something
$8 = 3751924
(gdb) nexti
0x08048775 18 : something(nValue), A(nValue)
(gdb) p something
$9 = 3751924
(gdb) nexti
20 cout << "B: " << dValue << endl;
(gdb) p something
$10 = 5