请考虑以下代码:
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的直接基础吗?
答案 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
的内容,则会有冲突实例化的定义(来自b
和c
)。 C ++通过强制继承链中派生类最多的类来实例化所有父类(如果d
没有显式实例化a
,上面会barf,但是a
提供了默认构造函数可以隐式使用)并忽略所有父实例化。
答案 1 :(得分:0)
为什么这样做?
根据标准(FIDS中的10.1.4),“对于指定为虚拟的每个不同的基类,最派生的对象应包含该类型的单个基类子对象”。
虚拟基础在从它派生的所有类之间共享,用于对象的实例。由于构造函数只能在对象的给定实例中调用一次,因此必须在最派生类中显式调用构造函数,因为编译器不知道有多少类共享虚拟基础。这是因为编译器将从最基类的构造函数开始,并且工作到派生程度最高的类。直接从虚拟基类继承的类不会通过标准调用它们的虚基类构造函数,因此必须显式调用它。
答案 2 :(得分:0)
来自N3337,12.6.2
初始化基础和成员
在类的构造函数定义中,直接和虚拟基础子对象的初始化程序和非静态数据成员可以由ctor-initializer指定,其格式为
也许拥有更好版本标准的人可以验证这一点。