我不明白为什么这个程序的输出如下.为什么没有编译错误?我想当尝试构造B时,编译器将找不到名为foo()的函数并报告错误.
#includeusing namespace std; struct A{ int a; A(int i=0) : a(i) { cout << "A" << endl; } ~A() { cout << "Bye A" << endl; } int foo() { return a; } }; struct B{ int b; B(int i=0) : b(i) { cout << "B" << endl; } ~B() { cout << "Bye B" << endl; } int bar() { return b; } }; struct C : B, A { C(int i=0) : B(foo()), A(i) {} }; int main() { cout << C(10).bar() << endl; return 0; }
输出:
B A 0 Bye A Bye B
一般来说,我想知道什么时候有多个继承,父结构的构造和初始化顺序是什么?我也可以期待类中的类似行为吗?
关于构造函数和析构函数调用的顺序的任何解释都非常感谢.
注意:这不是作业.而且,我已经研究了类似的主题,但没有提出任何关于这个问题.
您在foo
完全初始化对象之前通过调用来调用未定义的行为.引用C++标准中的12.6.2:
可以为正在构造的对象调用成员函数(包括虚拟成员函数,10.3).类似地,正在构造的对象可以是运算
typeid
符(5.2.8)或dynamic_cast
(5.2.7)的操作数.但是,如果在基类的所有mem-initializer完成之前,在ctor-initializer(或直接或间接从ctor-initializer调用的函数)中执行这些操作,则操作的结果是不确定的.[例如:
class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined: calls member function // but base A not yet initialized j(f()) { } // well-defined: bases are all initialized }; class C { public: C(int); }; class D : public B, C { int i; public: D() : C(f()), // undefined: calls member function // but base C not yet initialized i(f()) { } // well-defined: bases are all initialized };
- 结束例子]
换句话说,根据标准,这是可以的:
C(int i=0) : B(), A(i) { B::b = foo(); }
这将打印10
而不是0
你得到的(这可能是其他任何东西,因为这是未定义的行为).
抛开未定义行为的问题,并解决您的问题,初始化发生的顺序是明确定义的:
在非委托构造函数中,初始化按以下顺序进行:
- 首先,仅对于派生程度最高的类(1.8)的构造函数,虚拟基类按照它们出现在基类的有向无环图的深度优先从左到右遍历的顺序进行初始化,其中"左" -to-right"是派生类base-specifier-list中基类出现的顺序.
- 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中 (无论mem-initializers的顺序如何).
- 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何).
- 最后,执行构造函数体的复合语句.
[注意:声明顺序的作用是确保以初始化的相反顺序销毁基础和成员子对象. - 结束说明]
因此,在您的代码中,初始化顺序为:B
(B::b
),A
(A::a
),C
().
正如下面的评论中所指出的那样,更改此初始化顺序(例如,通过使用struct C : A, B
而不是struct C : B, A
)将不会消除未定义的行为.即使零件已初始化A::foo
,在B
零件初始化之前调用仍未定义A
.
这只是未定义行为的另一种情况.例如,我的系统给出了以下结果.
B A -858993460 Bye A Bye B
试试这个现场演示,它会产生另一个不同的结果(C(10).bar()
制作32764).
foo()
可以在此上下文中调用,但它将在 A
构造函数之前调用.这意味着a
被初始化,这导致读取未初始化的变量,这导致未定义的行为.这类似于在初始化之前访问成员.请考虑以下示例.a
初始化为b
's值,然后b
初始化.问题很明显,b
在读取初始化时未初始化a
.
struct foo { foo(int x) : a(b), b(x) {} int a; int b; }; int main() { foo bar(10); }