C++虛繼承的作用
C++虛繼承可以防止多重繼承產生的二義性問題。
虛繼承,就是在被繼承的類前面加上virtual關鍵字,這時被繼承的類稱為虛基類,如下面程式碼中的base類。虛繼承在多重繼承的時可以防止二義性。
class base
class derived1 : virutal public base
class derived2 : virtual public base
class derived3 : public derived1, public derived2
以上的程式碼如果用到了base中的某個成員變數就不會產生二義性。和#progma once在標頭檔案中的作用類似。請看下面的例子:
#include <iostream> using namespace std; class Parent { public: int p; // p將會被所有的子類繼承,也將是二義性的根源 inline Parent() { p = 10; } }; class Child1 : public Parent { public: int c1; inline Child1() { p = 12; // p在子類Child1中被賦值為12 c1 = 12; } }; class Child2 : public Parent { public: int c2; inline Child2() { p = 13; // p在子類Child2中被賦值為13 c2 = 13; } }; class GrandChild : public Child1, public Child2 { public: int grandchild; // p顯然也存在於GrandChild中,但是到底是12,還是13呢?這就產生了二義性 inline GrandChild() { grandchild = 14; } }; int main(void) { GrandChild* pGC = new GrandChild(); cout << pGC->p << endl; return 0; }
上面程式是不能通過編譯的,編譯器輸出的錯誤資訊如下:
…: error C2385: 'GrandChild::p' is ambiguous
…: warning C4385: could be the 'p' in base 'Parent' of base 'Child1' of class 'GrandChild'
…: warning C4385: or the 'p' in base 'Parent' of base 'Child2' of class 'GrandChild'
正如編譯器告訴我們的那樣,GrandChild::p是模稜兩可,它被Child1繼承了即Child1中包含了一個Parent subobject,也被Child2繼承了即Child2中也包含了一個Parent suboject,然後GrandChild又同時繼承了Child1和Child2,根據“derived class中要保持base class的完整原樣性原則
在上面的示例程式中做如下改動:
class Child1 : public Parent -> class Child1 :virtual public Parent
class Child2 : public Parent -> class Child2 :virtual public Parent
GrandChild的定義維持不變:
class GrandChild : public Child1, public Child2
做了上述改動後,即增加了兩個virtual關鍵字,再編譯就不會出現ambiguous之類的錯誤了。這是因為加上了virtual關鍵字後,就可以保證Parent suboject在GrandChild中只存在一份,從而消除了ambiguity。上面修改後的程式執行結果是13,這說明Child2類的那個p起了作用,如果GrandChild的定義寫成:
class GrandChild : public Child2, public Child1
那麼執行結果將是12。
上面的試驗結果表面,在多重繼承的時候,如果父類中有同名的成員變數(類似這篇文章中談及的例子),為了防止二義性,一般要採用虛繼承的方式,並且最右邊的基類中的那個成員變數會出現在派生類物件中。
另
---------------------------------------------------------------------------------
如果虛繼承之後,將cout << pGC->p << endl; 改為cout << pGC->Parent::p
<< endl; 或是cout << pGC->Child1(2)::p
<< endl;
輸出的p均為GrandChild中定義好的那個值