我有以下c ++代码(VS2013,如果重要的话):
#include <iostream>
using namespace std;
struct A{
A() { cout << "A()" << endl; }
A(int i) { cout << "A(" << i << ")" << endl; }
};
struct B : /*virtual*/ public A{
B(int i) : A(i){ cout << "B(" << i << ")" << endl; }
};
struct C : /*virtual*/ public A{
C(int i) : A(i){ cout << "C(" << i << ")" << endl; }
};
struct D : /*virtual*/ public B, /*virtual*/ public C{
D() : B(2), C(3){ cout << "D()" << endl; }
};
void main() {
D d;
system("pause");
}
该代码实现了菱形继承,总共有4个继承: B:A,C:A,D:B,D:C
如果没有将这些继承设置为virtual
,我会得到以下输出:
A(2)B(2)A(3)C(3)D()
这是有道理的。 D()首先调用B(int x),它首先调用A(int x),然后调用C(int x),它首先调用A(int x),最后调用D()本身。
但就虚拟继承而言,我有4个继承,这意味着我总共有16个虚拟\非虚拟继承组合。
弄乱不同的选项会产生一些非常意外的结果,而且我对虚拟遗传的了解越多,我就越感到困惑。
以下是一些例子:
virtual
时,我得到:A()B(2)A(3)C(3)D()
这是有道理的 - B虚拟地继承A,因此,除非另有说明,B(int x)调用A的默认构造函数。
virtual
时,我得到:A()A(2)B(2)C(3)D()
为什么A的构造函数都在B和C之前?这种行为对我没有意义。
virtual
时,我得到:A(3)C(3)A(2)B(2)D()
为什么C的构造函数会在B之前?
我可以继续下去。 其中一些组合会导致非常意外的结果(至少对我而言)。
我知道虚拟继承的基础知识,并在简单的例子中理解它们,我已经看到很多关于它的问题。但其中一些行为仍然困扰着我。
这些多个虚拟遗产是否有任何特定的规则?
编辑: 我被重定向到这个问题: Mixing virtual and non-virtual inheritance of a base class 虽然这有很大帮助,但我仍然对在任何特定情况下调用哪个A的构造函数感到困惑。 我还需要一些帮助。
答案 0 :(得分:2)
这里有两个大钥匙:
虚拟基类由最派生的类构造函数直接初始化,而不是由其他基类间接初始化。
虚拟基类在任何非虚拟基类之前初始化。
让我们看看你的例子。
当只设置B:A到虚拟时我得到:
A()B(2)A(3)C(3)D()
这是有道理的 - B虚拟地继承A,因此,除非另有说明,B(int x)调用A的默认构造函数。
B
根本不会调用A
的构造函数。 D
直接为A
的虚拟实例调用A
。但由于D
的构造函数未指定如何初始化A
,因此您获得A
的默认构造函数。
A
中的B(int i) : A(i)
被忽略,不是因为A
是虚拟基础,而是因为B
不是派生最多的类,因此无法构建它的A
。
当只设置C:A到虚拟时我得到:
A()A(2)B(2)C(3)D()
为什么A的构造函数都在B和C之前?这种行为对我没有意义。
此处C::A
是唯一的虚拟基类,因此首先进行初始化。同样,由于D
的构造函数没有为A
指定初始值设定项,因此该虚拟子对象使用默认构造函数。
然后非虚拟基类按顺序排列。 B
之前有C
,但B
首先需要初始化其非虚拟A
(2
)。
当只将D:C设置为虚拟时,我得到:
A(3)C(3)A(2)B(2)D()
为什么C的构造函数会在B之前?
唯一的虚拟基类是C
,因此它会在B
之前构建。但是这次C
的构造函数必须首先初始化其非虚拟基础子对象C::A
。然后是B
,必须首先初始化其非虚拟子对象B::A
。
最后,存在“正常”模式,这使得A
两个继承都是虚拟的,只产生一个A
子对象,这是虚拟继承的整个点。使B
或C
虚拟是毫无意义的,除非您希望D
以更复杂的方式重用为基类。
#include <iostream>
using namespace std;
struct A{
A() { cout << "A()" << endl; }
A(int i) { cout << "A(" << i << ")" << endl; }
};
struct B : virtual public A{
B(int i) : A(i){ cout << "B(" << i << ")" << endl; }
};
struct C : virtual public A{
C(int i) : A(i){ cout << "C(" << i << ")" << endl; }
};
struct D : public B, public C{
D() : A(1), B(2), C(3){ cout << "D()" << endl; }
};
void main() {
D d;
system("pause");
}
输出:
A(1)B(2)C(3)D()
注意我已向A(1)
添加了初始化程序D
,以表明虚拟子对象不一定需要使用默认构造函数。您可以在任何示例中完成此操作,其中至少有一个A
继承是虚拟的。