通话单.cpp
#include <iostream>
class A
{
public:
A()
{
std::cout << "A" ;
}
};
class B: public A
{
public:
B()
{
std::cout << "B" ;
}
};
class C: virtual public A
{
public:
C()
{
std::cout << "C" ;
}
};
class D: public B, public C
{
public:
D()
{
std::cout << "D" ;
}
};
int main()
{
D d;
return 0;
}
编译
g++ order-of-call.cpp -std=c++11
输出
AABCD
为什么两个A
在一起输出?我期待的是类似ABACD
的东西。但是如果我这样改变继承顺序
class D: public C, public B
,输出如预期的ACABD
。是订单的一部分,还是g ++的专有内容。
答案 0 :(得分:6)
这是有道理的,因为虚拟基类是在非虚拟基类之前构造的。因此,根据您的情况,它是:virtual A
,non-virtual A
,BCD
。如果更改继承顺序,则顺序为virtual A
,C
,non-virtual A
,BD
。检出此内容:https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/cplr389.htm
初始化类的顺序如下:
- 虚拟基类的构造函数按照它们在基列表中出现的顺序执行。
- 非虚拟基类的构造函数按声明顺序执行。
- 类成员的构造函数以声明顺序执行(无论其在初始化列表中的顺序如何)。
- 构造函数的主体被执行。
答案 1 :(得分:4)
这似乎很相关:https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-ctor-order,尤其是:
要执行的第一个构造函数是层次结构中任何位置的虚拟基类。
所有虚拟基类构造函数完成后,构造顺序通常是从基类到派生类。因此,如果类D继承自B1和B2的乘法,则首先执行B1的构造函数,然后执行B2的构造函数,然后执行D的构造函数。
请注意,顺序B1和B2(或B1a然后B1b)由基类在类的声明中出现的顺序确定。
因此,您看到的第一个A
总是通过C->A
的虚拟继承。休息通常是从左到右深入。
答案 2 :(得分:1)
构建顺序首先是虚拟基础,然后按从左到右的顺序(递归)深入构建非虚拟基础。
因此,在构造D
时,将首先构造类A
的虚拟基数C
(因此将输出第一个'A'
)。然后开始在B
中构建D
,这首先构造了非虚拟A
(输出第二个'A'
),它是{{1 }},然后调用B
的构造函数(输出B
)。然后调用'B'
的构造函数-它的基础C
是虚拟的,因此不再构造,然后调用A
的构造函数(输出C
)。最后,调用'C'
的构造函数(输出'D'
)。
答案 3 :(得分:-1)
如果您在此处添加缺少的虚拟:
class B: virtual public A
这将解决多重继承问题,并且每个类将获得一个A实例
然后输出将是简单的ABCD或更改订单的情况
class D: public C, public B
输出:
ACBD
符合标准,此行为独立于您使用的编译器
但是,如果确实需要,则仅对于C类是虚拟的。在任何情况下都将首先调用虚拟承包商,然后根据标准再次调用它:
ISO / IEC JTC1 SC22 WG21 N 3690
在非委托构造函数中,初始化在 以下顺序:
首先,仅适用于大多数 派生类(1.8),虚拟基类按以下顺序初始化 它们出现在定向的深度优先的从左到右的遍历中 基类的非循环图,其中“从左到右”是 基类在派生类中的外观 base-specifier-list。
然后,直接基类在 它们出现在基本说明符列表中的声明顺序 (与mem-initializers的顺序无关)。
- 然后,非静态数据成员将按照它们在 类定义(同样,无论顺序如何 mem-initializers)。
- 最后, 构造函数体被执行。