1. 程式人生 > >繼承與派生(4):二義性(三角繼承和菱形繼承)

繼承與派生(4):二義性(三角繼承和菱形繼承)

         一般說來,在派生類中對基類成員的訪問應該是唯一的,但是,由於多繼承情況下,可能造成對基類中某成員的訪問出現了不唯一的情況,則稱為對基類成員訪問的二義性問題。  實際上,在上例已經出現過這一問題,回憶一下上例中(參照繼承和派生(3)最後一個例子),派生類A的兩基類B1和B2中都有一個成員函 數print()。如果在派生類中訪問print()函式,到底是哪 一個基類的呢?於是出現了二義性。但是在上例中(三角繼承)解決了這個問題,其辦法是通過作用域運算子::進行了 限定。如果不加以限定,則會出現二義性問題。 


菱形繼承

#include <iostream>
using namespace std;

class A      
{ public:  
    A(int i){a=i;cout<<"con. A\n";} 
	void print(){cout<<a<<endl;}  
	~A(){cout<<"des. A\n";} 
  private:  
	  int a;
};    
class B1 : public A      
{  public:        
     B1(int i, int j) : A(i){b1=j;cout<<"con. B1\n";}    
	 void print(){A::print(); cout<<b1<<endl;}   
	 ~B1(){cout<<"des. B1\n";}    
  private:    
	  int b1; 
}; 

class B2 : public A 
{  public:  
     B2(int i, int j) : A(i){b2=j;cout<<"con. B2\n";} 
     void print(){A::print(); cout<<b2<<endl;}  
	 ~B2(){cout<<"des. B2\n";} 
   private:  
	   int b2;
}; 
class C : public B1, public B2 
{  public: 
       C(int i, int j, int k, int l, int m) : B1(i,j), B2(k,l), c(m)  {cout<<"con. C"<<endl;} 
	   void print()  {B1::print(); B2::print(); cout<<c<<endl; }  
	   ~C(){cout<<"des. C"<<endl;} 
   private: 
	   int c; 
};
int main() 
{  C c1(1,2,3,4,5);
   c1.print();
} 

 

重複繼承--虛基類 
• 下面的類D從類A繼承兩次,稱為重複繼承:

class A

{  int x;  ...... };

class B: public A

{ ... };

class C: public A

{ ... };

class D: public B, public C

{ ... };

• 如果需要類D中只有一個x,則應把A定義為 B和C的虛基類:

 class B: virtual public A {...};

 class C: virtual public A {...};

class D: public B, public C {...}; 

• 上面的類D將包含兩個x成員:B::x和C::x。 

虛基類的建構函式  
            C++規定,虛基類子物件是由最派生類的構造 函式通過呼叫虛基類的建構函式進行初始化的。如 果一個派生類有一個直接或間接的虛基類,那麼派生類的建構函式的成員初始列表中必須列出對虛基類建構函式的呼叫。如果未被列出,則表示使用該 虛基類的預設建構函式來初始化派生類物件中的虛基類子物件。  
       從虛基類直接或間接繼承的派生類中的 建構函式的成員初始化列表中都要列出這個虛基類建構函式 的呼叫。但是,只有用於 建立物件的那個最派生類的建構函式呼叫虛 基類的建構函式,而該派生類的基類中所列 出的對這個虛基類的建構函式呼叫在執行中 被忽略,這樣便保證了對虛基類的物件只初 始化一次。

       C++又規定,在一個成員初始化列表中 出現對虛基類和非虛基類建構函式的呼叫,則虛基類的建構函式先於非虛基類的建構函式的執行。 

#include <iostream> 
using namespace std;
class A 
{ public:  
      A(const char  *s)          
	  {cout<<s<<endl;}  
	  ~A(){} 
};
class B : virtual public A
{ public:  
     B(const char *s1, const char * s2) : A(s1)  { cout<<s2<<endl;}
}; 
class C : virtual public A 
{ public:          
     C(const char *s1,const char * s2) : A(s1)  {cout<<s2<<endl;} 
}; 
class D : public B, public C 
{ public:   
     D(const char * s1,const char * s2,const char * s3,const char * s4): B(s1,s2),C(s1,s3), A(s1)//虛基類先於非虛基類      
	 { cout<<s4<<endl; }
}; 

void main() 
{ 
	D* ptr=new D("class A","class B","class C","class D");
	delete ptr; 
} 


 

在派生類B和C中使用了虛基類,使得 建立的D類物件只有一個虛基類子物件。         在派生類B,C,D的建構函式的成員 初始化列表中都包含了對虛基類A的構造 函式。     在建立類D物件時,只有類D的構造 函式的成員初始化列表中列出的虛基類構 造函式被呼叫,並且僅呼叫一次,而類D 基類的建構函式的成員初始化列表中列出 的虛基類建構函式不被執行。這一點將從 該程式的輸出結果可以看出。