钻石继承幕后发生了什么?

时间:2014-08-21 08:17:02

标签: c++ inheritance

我使用钻石继承在C ++中编写了一个代码,而没有使用关键字'virtual'作为最顶层的A类,同时定义继承A的B和C类。然后我定义了继承自B和C的类D.类D不使用类A成员x,编译不会抛出任何错误。但如果确实如此(通过取消注释函数foo()),编译器会抛出提及歧义的错误。

#include<iostream>
#include<cstdlib>

using namespace std;

class A
{
protected:
  int x;
public:
  void set(int y){x=y;}
};

class B:public A
{
};

class C:public A
{
};

class D:public B,public C
{
public:
  // void foo(){x*=2;} // error!
};

int main()
{
  cout<<sizeof(D)<<endl;
  return(0);
}

在定义C和D类时,使用'virtual'限定符允许使用foo()函数,如下面C类所示。

class C:virtual public A
{
};

现在foo()中使用的x没有任何歧义,因为它只有一个副本。我的问题是这样的:

a)在第一种情况下,编译器实际上是否允许2份x副本,因为它没有解决任何歧义?

b)如果对(a)的答案是肯定的,那么为什么第一个代码输出8代表D类的大小而第二个代码输出24呢?由于第一种情况有x的两个副本,它的大小不应该至少与第二种情况一样多(即使我们忽略填充)?

1 个答案:

答案 0 :(得分:3)

a)是的。它实际上是重复的。为了明确,D包含成员B :: x和C :: x。

如果你修改foo()是显式的,它可以工作。例如:

void foo() { B::x *= 2; }

b)在虚拟继承的情况下占用额外内存的原因是需要虚拟表或vtable。

您可以在此处找到一个很好的解释:http://www.cprogramming.com/tutorial/virtual_inheritance.html