多繼承引起的二義性
多重繼承可以反映現實生活中的實際情況,能有有效的處理一些較復雜的問題,使編程具有靈活性。但是多重繼承也引起了一些值得註意的問題,它增加了程序的復雜度,使程序的編寫和維護變得相當困難。其中最常見的問題就是繼承成員同名而產生的二義性(Ambiguous)問題。
①調用不同基類的同名成員時可能出現二義性
1 class A 2 { 3 public: 4 void setA(int a); 5 int get(); 6 private: 7 int a; 8 } ; 9 class B 10 { 11 public: 12 void setB(int b); 13 int get(); 14 private: 15 int b; 16 } ; 17 18 class C:public A,public B 19 { 20 public: 21 void setC(int c); 22 int getC(); 23 private: 24 int c; 25 };
在執行obj.get();時將是有二義性的。因為類C分別從類A類B繼承了兩個不同版本的get()成員函數,因此,obj.get();到底調用哪個get()版本,編譯器將無從知曉。
對於這種二義性問題,常見有兩種解決方法:
(1)使用作用域分辨符::加以消除。
obj.A::get();
obj.B::get();
(2)在類C中也定義成員函數get()函數,則有類C的對象obj訪問get()函數obj.get()沒有二義性,這是因為當派生類中的成員與基類中的成員重名時,派生類中的同名成員將被調用。
1 class A 2 { 3 public: 4 void setA(int a); 5 int get(); 6 private: 7 int a; 8 } ; 9 class B10 { 11 public: 12 void setB(int b); 13 int get(); 14 private: 15 int b; 16 } ; 17 18 class C:public A,public B 19 { 20 public: 21 void setC(int c); 22 int get(); 23 //此處改為這樣 24 private: 25 int c; 26 };
②訪問共同基類的成員時可能出現二義性
當一個派生類有多個基類,而這些基類又有一個共同的基類時,也就是所謂的菱形繼承。這時對這個共同基類中成員的訪問可能出現二義性。
1 class A 2 { 3 public: 4 void disp(); 5 private: 6 int a; 7 }; 8 9 class B1:public A 10 { 11 public: 12 void dispB1(); 13 private: 14 int b1; 15 }; 16 class B2:public A 17 { 18 public: 19 void dispB2(); 20 private: 21 int b2; 22 }; 23 24 class C:public B1,public B2 25 { 26 public: 27 void dispC(); 28 private: 29 int c; 30 };
在此類結構下,如果創建類C的對象c1:
C c1;
則下面的兩個訪問都有二義性:
c1.disp();
c1.A::disp();
這是因為B1,B2分別從類A中繼承了一個disp()成員函數的副本,因此類C中就有了分別從類B1,B2兩條不同路線上繼承過來的disp()版本,盡管這兩個版本的函數完全相同,但是語句“c1.disp();”將使編譯器無從知曉到底調用從類B1繼承來的disp(),還是調用從類B2繼承來的disp(),這就是導致二義性的原因。
語句“c1.A::disp();”產生二義性的道理相同,不過下面的兩條調用語句卻是正確的:
c1.B1::disp();
c1.B2::disp();
因為通過“B1::”及“B2::”的限定,明確告訴編譯器應該調用從哪條路徑上繼承過來的disp()。
在一個類中保留間接共同基類 的多份同名成員,雖然有時是必要的,可以在不同的數據成員中分別存放不同的數據,也可以通過構造函數分別對它們進行初始化。但在大多數情況下,這種現象是人們不希望出現的。
因為保留多份數據成員的副本,不僅占用較多的存儲空間,還增加了訪問這些成員時的困難。而且在實際上,並不需要有多份副本,為此,c++中提供了虛基類(Virtual Base Class)技術來解決這個問題。
關於虛基類方面的內容請跳轉至http://www.cnblogs.com/tenjl-exv/p/7625836.html
多繼承引起的二義性