c ++多重继承和显式构造函数调用

时间:2018-04-30 16:47:03

标签: c++ inheritance constructor compiler-errors multiple-inheritance

考虑我们有一个父类A,它有受保护的成员x和y以及一个构造函数:

A::A(int xpos, int ypos) : x(xpos), y(ypos) {}

现在考虑我们有两个继承自A的B和C类,并且具有如下定义的构造函数。

class B : public virtual A
B::B(int xpos, int ypos) : A(xpos,ypos) {}

class C : public virtual A
C::C(int xpos, int ypos) : A(xpos,ypos) {}    

最后,让我们有一个继承B和C的D类。

class D : public B, public C

如果我按如下方式编写Ds构造函数,我会收到编译错误,说Ds构造函数必须显式调用As构造函数。

D::D(int xpos, int ypos) : B(xpos,ypos), C(xpos,ypos) {}  

这是为什么?当我试图继承我对类构造函数一无所知的类时,这肯定会有问题吗?难道B和C都明确地调用As构造函数吗?

2 个答案:

答案 0 :(得分:2)

虚拟基础的规则是派生程度最高的类调用其构造函数。如果不是这样的话,哪个中间基类应该构建虚拟基础的一个副本?假装您的类B的构造函数在构造A时反转参数的顺序:

B::B(int xpos, int ypos) : A(ypos, xpos) {}

现在,A子对象的状态应该是什么?第一个构造函数获胜?最后一个获胜?无论哪种方式,它都很脆弱:更改B类型中CD的顺序会改变A基类的初始化方式。

答案 1 :(得分:1)

BC明确调用A的构造函数是不够的,因为A是继承的,而且是虚拟的" 。这意味着A的单个实例的内容是"共享"在BC之间。

如果C ++允许BC初始化"共享"单方面A的实例,你可能会得到不一致的行为。考虑如果D的构造函数在两个初始值设定项中切换xposypos的顺序会发生什么:

D::D(int xpos, int ypos) : B(xpos,ypos), C(ypos,xpos) {}

由于BC转发xposypos转移到A::AA的实例在B之间共享并且C不会以编组方式初始化。这就是为什么D::D需要明确初始化A的虚拟实例的原因。

请注意,只有在处理虚拟继承时才需要这样做,因为BC实例的一部分是共享的。

  

如果我尝试继承我对类构造函数一无所知的类,这肯定会有问题吗?

调用base-of-base构造函数的要求确实打破了封装。但是,当您继承具有虚拟基础的类时,继承者不仅要了解其兄弟基础的结构和行为,还要了解它们之间的相互作用。这就是为什么打破封装可以被认为是合理的,即使它并不理想。