为什么钻石继承只有一个虚拟继承?

时间:2016-01-10 05:10:47

标签: c++ multiple-inheritance diamond-problem

假设我们有经典的多继承模式:

class Base {
    int baseMember;
};

class A : public Base {
    int aMember;
};

class B : public Base {
    int member;
};

class Derived : public A, public B {
    int derivedMember;
};

这将在内存中列出Derived对象:

  • Base的字段< - (Base *)(A *)this | (A *)这个|此
  • A的字段
  • Base的字段< - (Base *)(B *)this | (B *)这
  • B的字段
  • 派生的字段

为了防止Base的字段被复制,默认方法是从A和B虚拟地继承Base:

class A : public virtual Base {
...
class B : public virtual Base {

这将导致以下布局:

  • 偏移到Base的字段< - (A *)this |此
  • A的字段
  • 偏移到Base的字段< - (B *)this
  • B的字段
  • 派生的字段
  • Base的字段< - (Base *)this | (基*)(A *)这个| (基本*)(B *)这

这解决了这个问题,需要权衡你必须应用偏移来从其他类访问Base的字段(和虚函数)。

但为什么以下情况不可能?

  • Base的字段< - (Base *)(B *)this | (基*)(A *)这个| (A *)这个|此
  • A的字段
  • 偏移到Base的字段< - (B *)this
  • B的字段
  • 派生的字段

这将允许A和Derived在没有开销的情况下访问Base,没有重复,甚至将大小缩小1整数。但是,如果我尝试它,编译器仍然会复制Base的字段(将它们放在B的字段后面)。

note :我知道有类似的问题,但我没有看到为什么这是不可能的。

1 个答案:

答案 0 :(得分:2)

在看到Derived之前,你需要布置A和B.一旦你选择了布局,它就是一成不变的。派生和所有其他类必须将它用于A和B子对象。 (虚拟基本位置可能因每个完整的类而异,并且不被视为布局的一部分。)

现在,在虚拟继承的情况下如何布置A?它必须包含offset-to-Base字段,因为我们不知道A将如何在程序的其他部分中使用。它可能是某些尚未编写的派生类的第一个基类,或者是第七个。但是使用A的函数现在必须有一种方法将A转换为Base,而无需等待定义这些派生类。

关于B.

当然也是如此

所以A和B都是自己的offsset-to-Base字段。 Derived无法做出其他决定,因为它可能不是继承A或B的唯一类。