虚基继承和基类的基类

时间:2014-12-05 13:42:22

标签: c++

请考虑以下代码:

class A {
};

class B : public A {
};

class C : public B{ 
    public:
    C() : A() {}  // ERROR, A is not a direct base of B
};

在这种情况下,GCC(4.8.1,C ++ 99)给出了正确的错误(我理解这种行为):

prog.cpp:12:8:错误:输入'a'不是'c'的直接基础

但是,如果b和a之间的继承是虚拟的,则不会发生这种情况:

class A {
};

class B : virtual public A {
};

class C : public B{
    public:
    C() : A() {}  // OK with virtual inheritance
};

为什么这样做? A现在被编译器视为C的直接基础吗?

3 个答案:

答案 0 :(得分:2)

一般情况下,因为这就是C ++尝试解决钻石继承问题http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem的方法(无论是好的还是坏的解决方案都留给了读者)。

所有继承都是is-a和has-a关系的组合......您必须实例化父实例。如果您有以下课程:

class a;
class b : a;
class c : a;
class d : b,c;

然后你为每个b和c实例化了一个a。 d不知道使用哪个。

C ++通过允许虚拟继承来解决这个问题,这是一种高开销继承,它允许b和c共享相同的a,如果继承在d中(它比这复杂得多,但你可以自己阅读) )。

链中派生类型最多的类型需要能够覆盖共享类的实例化,以便以在父类中继承共享类的方式来控制差异。请看以下示例:

class a {int x; public: a(int xx) {x=xx;} int get_x() {return x;}};
class b : public virtual a { public: b(): a(10){}};
class c : public virtual a { public: c(): a(15){}};
class d : public virtual b, public virtual c {public: d() : a (20) {}};
int main() {
    d dd;
    std::cout << dd.get_x() << std::endl;//20, d's constructor "wins"
    return 0;
}

如果d没有定义实例化a的内容,则会有冲突实例化的定义(来自bc)。 C ++通过强制继承链中派生类最多的类来实例化所有父类(如果d没有显式实例化a,上面会barf,但是a提供了默认构造函数可以隐式使用)并忽略所有父实例化。

答案 1 :(得分:0)

为什么这样做?

根据标准(FIDS中的10.1.4),“对于指定为虚拟的每个不同的基类,最派生的对象应包含该类型的单个基类子对象”。

虚拟基础在从它派生的所有类之间共享,用于对象的实例。由于构造函数只能在对象的给定实例中调用一次,因此必须在最派生类中显式调用构造函数,因为编译器不知道有多少类共享虚拟基础。这是因为编译器将从最基类的构造函数开始,并且工作到派生程度最高的类。直接从虚拟基类继承的类不会通过标准调用它们的虚基类构造函数,因此必须显式调用它。

答案 2 :(得分:0)

来自N3337,12.6.2

  

初始化基础和成员

     

在类的构造函数定义中,直接和虚拟基础子对象的初始化程序和非静态数据成员可以由ctor-initializer指定,其格式为

也许拥有更好版本标准的人可以验证这一点。