虚拟inheritance如何解决“菱形”(多重inheritance)模糊性?

class A { public: void eat(){ cout<<"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,C { public: void eat(){ cout<<"D";} }; int main(){ A *a = new D(); a->eat(); } 

我了解钻石问题,上面的代码没有这个问题。

虚拟inheritance如何解决问题?

我的理解是:当我说A *a = new D(); ,编译器想知道typesD的对象是否可以分配给typesA的指针,但它有两条可以遵循的path,但是不能自行决定。

那么,虚拟inheritance如何解决这个问题(帮助编译器做出决定)呢?

你想:(用虚拟inheritance来实现)

  D / \ BC \ / A 

而不是:(没有虚拟inheritance会发生什么)

  D / \ BC | | AA 

虚拟inheritance意味着只有一个基类A实例不是2。

你的typesD有两个vtable指针(你可以在第一个图中看到它们),一个用于B ,一个用于虚拟inheritanceA C D的对象大小增加,因为它现在存储2个指针; 但现在只有一个A

所以B::AC::A是相同的,所以不能有D模棱两可的调用。 如果你不使用虚拟inheritance,你有上面的第二个图。 而任何对A成员的调用都变得模糊不清,你需要指定你想要的path。

维基百科有另一个很好的概要和例子在这里

派生类的实例“包含”基类的实例,所以它们像这样查找内存:

 class A: [A fields] class B: [A fields | B fields] class C: [A fields | C fields] 

因此,如果没有虚拟inheritance,D类的实例将如下所示:

 class D: [A fields | B fields | A fields | C fields | D fields] '- derived from B -' '- derived from C -' 

所以,请注意A数据的两个“副本”。 虚拟inheritance意味着派生类内部有一个在运行时设置的指向基类数据的vtable指针,以便B,C和D类的实例如下所示:

 class B: [A fields | B fields] ^---------- pointer to A class C: [A fields | C fields] ^---------- pointer to A class D: [A fields | B fields | C fields | D fields] ^---------- pointer to B::A ^--------------------- pointer to C::A 

问题不是编译器必须遵循的path 。 问题是这个path的终点 :这个结果。 当谈到types转换时,path并不重要,只有最终的结果。

如果你使用普通的inheritance,每个path都有自己独特的端点,这意味着转换的结果是不明确的,这是问题。

如果使用虚拟inheritance,则会得到一个菱形层次结构:两个path通向相同的端点。 在这种情况下,selectpath的问题不再存在(或者更确切地说,不再重要),因为两条path都会导致相同的结果。 结果不再含糊不清 – 那才是最重要的。 确切的path不是。

其实这个例子应该如下:

 #include <iostream> //THE DIAMOND PROBLEM SOLVED!!! class A { public: virtual ~A(){ } virtual void eat(){ std::cout<<"EAT=>A";} }; class B: virtual public A { public: virtual ~B(){ } virtual void eat(){ std::cout<<"EAT=>B";} }; class C: virtual public A { public: virtual ~C(){ } virtual void eat(){ std::cout<<"EAT=>C";} }; class D: public B,C { public: virtual ~D(){ } virtual void eat(){ std::cout<<"EAT=>D";} }; int main(int argc, char ** argv){ A *a = new D(); a->eat(); delete a; } 

…这样的输出将是正确的:“EAT => D”

虚拟inheritance只能解决祖父的重复! 但是你仍然需要指定方法是虚拟的,才能正确地覆盖方法…

这个问题可以通过使用Virtual关键字来解决。

  A / \ BC \ / D 

钻石问题的例子。

 #include<stdio.h> using namespace std; class AA { public: int a; AA() { a=10; } }; class BB: virtual public AA { public: int b; BB() { b=20; } }; class CC:virtual public AA { public: int c; CC() { c=30; } }; class DD:public BB,CC { public: int d; DD() { d=40; printf("Value of A=%d\n",a); } }; int main() { DD dobj; return 0; } 
 #include <iostream> class A { public: virtual ~A(){ } virtual void eat(){ std::cout<<"EAT=>A";} }; class B: virtual public A { public: virtual ~B(){ } virtual void eat(){ std::cout<<"EAT=>B";} }; class C: virtual public A { public: virtual ~C(){ } virtual void eat(){ std::cout<<"EAT=>C";} }; class D: public B,C { public: virtual ~D(){ } }; int main(int argc, char ** argv){ A *a = new D(); a->eat(); delete a; }