1. 程式人生 > >C++虛繼承的作用

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的完整原樣性原則

”,因此GrandChild包含了兩個ParentObject。所以當pGC->p時,編譯器根本無法確定是呼叫Child1::p還是Child2::p,由此邊產生了模稜兩可的情形。怎麼解決這樣的問題呢?答案就是用虛繼承或者叫虛基類的方式。

在上面的示例程式中做如下改動:

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中定義好的那個值