#include <iostream>
using namespace std;

class A             { public: void eat(){ cout<<"A";} };
class B: public A   { public: void eat(){ cout<<"B";} };
class C: public A   { public: void eat(){ cout<<"C";} };
class D: public B,C { public: void eat(){ cout<<"D";} };

int main(){
    A *a = new D();
    a->eat();
}

我不知道这就是所谓的钻石的问题或没有,但为什么不这项工作?

我已经给出了对于eat()D认定中。因此,它不需要为使用B的或C的副本(所以,应该没有问题)。

:当我说,a->eat()(记住eat()不是虚拟的),只有一个可能的eat()到呼叫,即A的。

那么,为什么我得到这个错误:

  

'A' 是 'd'

的暧昧基

什么A *a = new D();平均究竟给编译器??

为什么不当我使用D *d = new D();发生同样的问题?

有帮助吗?

解决方案

设想一个稍微不同的情形

class A             { protected: int a; public: void eat(){ a++; cout<<a;} };
class B: public A   { public: void eat(){ cout<<a;} };
class C: public A   { public: void eat(){ cout<<a;} };
class D: public B,C { public: void eat(){ cout<<"D";} };

int main(){
    A *a = new D();
    a->eat();
}

如果这会工作,将它递增aBaC?这就是为什么它是不明确的。的this指针和任何非静态数据成员是不同的两个A子对象(其中之一是由B子对象包含的,而另一个由C子对象)。试着改变你这样的代码,它会工作(因为它编译并打印“A”)

class A             { public: void eat(){ cout<<"A";} };
class B: public A   { public: void eat(){ cout<<"B";} };
class C: public A   { public: void eat(){ cout<<"C";} };
class D: public B, public C { public: void eat(){ cout<<"D";} };

int main(){
    A *a = static_cast<B*>(new D());
      // A *a = static_cast<C*>(new D());
    a->eat();
}

这将分别呼吁eatAB子对象C

其他提示

在金刚石导致在d对象A的两个实例,并且它是不明确的,其中一个你指的是 - 你需要使用虚拟继承来解决这个问题:

class B: virtual public A   { public: void eat(){ cout<<"B";} };
class C: virtual public A   { public: void eat(){ cout<<"C";} };

假设你其实只是想一个实例。我还假设你的真正用意:

class D: public B, public C { public: void eat(){ cout<<"D";} };

请注意编译错误是在 “A * A =新d();”线,而不是在呼叫“吃”。

的问题是,因为您使用的非虚拟继承,则与A类最终两次:一次通过B,并且一旦通过C.例如,如果你添加成员m至A,然后d具有其中的两个: B :: M和C ::米。

有时候,你真的想有一个两倍的推导图,在这种情况下,你总是需要指出其中A你正在谈论。在d,你将能够参考B ::分别M和C ::米。

不过,有时候,你真的想只有一个,在这种情况下,你需要使用的虚拟继承

对于一个真正不寻常的情况,尼尔的答案其实是错误的(至少部分)。

退出虚拟继承,你A的两个单独的副本中的最终目标。

“钻石”导致A在最终对象的单一拷贝,并产生通过使用虚拟继承:

“替代文字”

由于“金刚石”是指只有一个在最终对象A的复制,以A参考不产生歧义。如果没有虚拟继承,向A参考可以指代两个不同的对象(在一个在左边或在图中右边的一个)的

你得到的错误不是从调用eat()未来 - 这是来自前一行的到来。这是上溯造型本身造成模糊不清。正如尼尔·巴特沃思指出的,在你的A D的两个副本,以及编译器不知道你想要哪一个在a来点。

您想:(可实现与虚拟继承)

  

d,点击   / \结果   B C点击   \ /点击   甲

而非(没有虚拟继承会发生什么)

  

d,点击   / \结果   B C点击   | |点击   A A

虚拟继承意味着将只有1基A类不2.

的实例

您类型D将有2个虚表指针(你可以看到他们的第一个图表),一个用于B,一个用于C谁几乎继承AD的对象大小增加,因为现在存储2个球;然而,只有一个A现在。

因此B::AC::A是相同的,所以不可能有从D无歧义的呼叫。如果你不使用虚拟继承你有上面的第二个图。和A的成员的任何呼叫,则变得模糊,你需要指定要该走的路。

维基百科有另一个很好的破旧和例如此处

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top