1.何為菱形繼承?
兩個子類繼承同一個父類,而又有子類又分別繼承這兩個子類,就如上圖說示。

#include<stdio.h> #include<iostream> #include<queue> using namespace std; class A { public: A(){printf("A create.\n");} int a; virtual void fun(){} }; class B: public A{ public: B(){printf("B create.\n");} int b; virtual void fun1(){} }; class C: public A { public : int c; C(){printf("C create.\n");} virtual void fun3(){printf("fun3 .\n");} }; class D:public C,public B{ public: int d; D(){printf("D create.\n");} virtual void fun3(){printf("fun4 .\n");} }; //二義性問題的開銷 int main() { D *pd=new D; printf("%d\n",sizeof(D)); getchar(); }
產生的問題,會產生二義性問題,即對於baseClass的調用要說明作用域的情況:
D *pd=new D; pd->B::a=1; pd->C::a=2; printf("%d\n",pd->B::a); printf("%d\n",pd->C::a);
相當於baseClass在類中有兩個,這可能不是我們想要的結果,增加調用的困難,同時也會浪費內存資源。
這種結構如圖:
可以看到A指向的虛函數表的位置是不一樣的!即baseClass有兩個實例!
2.如何解決?
使用虛擬繼承!

#include<stdio.h> #include<iostream> #include<queue> using namespace std; class A { public: A(){printf("A create.\n");} int a; virtual void fun(){} }; class B:virtual public A{ public: B(){printf("B create.\n");} int b; virtual void fun1(){} }; class C:virtual public A { public : int c; C(){printf("C create.\n");} virtual void fun3(){printf("fun3 .\n");} }; class D:public C,public B{ public: int d; D(){printf("D create.\n");} virtual void fun3(){printf("fun4 .\n");} }; //二義性問題的開銷 int main() { D *pd=new D; pd->B::a=1; pd->C::a=2; printf("%d\n",pd->B::a); printf("%d\n",pd->C::a); printf("%d\n",sizeof(D)); getchar(); }
內存布局:
對於baseClass是公用的,也就是baseClass就實例化了一個對象!想想這會有什么后果?調用B,C的虛函數的時候就一個虛表怎么行,所以有需要對應有兩個相應的虛表指向B,C,於是就成了上面的結構了。
調試觀察,果然如此!
總結:可以通過虛擬繼承消除二義性,但是虛擬繼承的開銷是增加虛函數指針。
參考:C++程序優化
ps:這時就有疑問了,既然多重繼承的會有二義性的問題,為什么編譯器不能自己識別並處理呢,也許別人給你寫的一個類中與你寫的類中同時繼承了一個基類(不知情),別人又同時繼承了這兩個類= =!可能有人會說,多重繼承太復雜,一般都用單一繼承!也許這就是C++優勢有時也是劣勢吧= =