C++多重繼承相關問題

  多重繼承的問題,首先想到多繼承構造函數如何處理?

  Symbian下是可以一個C類多個M類多重繼承的

  C++中的多繼承,構造函數處理並沒有問題,對象構造的時候按照繼承中聲明的順序調用多個父類的構造函數,析構函數同樣遵守單繼承中的原則。

二意性問題

  如果多基類中存在同名成員,會產生二意性的問題

  比如,Root1類中聲明DoAny()接口,Root2類中也聲明瞭DoAny()接口,Child多承繼Root1和Root2,那麼如果Child對象直接調用DoAny接口就會產生編譯錯誤。

  深入原因??

  對象值給父類指針、引用產生的二義性問題

  繼續上面的例子,Child對象的引用可以賦給Root1和Root2類引用。

  如果存在void display(const Root1&),void display(cont Root2&)兩個接口,那麼Child child;    display(child);這個調用也會產生二義性錯誤,編譯器不知道應該調用哪個接口。

  

名字查找過程

  查找過程是一個域一個域的查找,直到全局域。每個類都有一個類域。看如下示例代碼:

class ZooAnimal {
public:
	void print() const;
	// 爲了向外域暴露設爲 public
	string is_a;
	int ival;
private::
	double dval;
};

class Bear : public ZooAnimal {
public:
	void print() const;
	int mumble( int );
	// 爲了向外域暴露設爲 public
	string name;
	int ival;
};
Bear bear;    bear.is_a;

  如上調用,is_a的名字解析過程爲:

  1. 首先查找Bear類域,沒有找到

  2. 查找Bear父類ZooAnimal類域,找到其聲明,名字解析成功。

bear.ival;

  這個調用解析過程如何呢?因爲Bear和其父類ZooAnimal裏都有該成員定義。答案是按照上的順序查找,查找到馬上返回。

  1. Bear類域中查找,找到ival聲明,解析成功,不再向下查找。那麼父類中名字就被子類中定義屏蔽了。

  需要用bear.ZooAnimal::ival的形式來域限定符訪問ZooAnimal類中成員ival,這樣編譯器直接在ZooAnimal類域中開始查找名字。

看如下調用各名字的位置:

  int ival;

  int Bear::mumble(int ival)

  {

    return  ival + //形參

    ::ival + //全局成員

    ZooAnimal::ival +

    Bear::ival;

  }

而如下名字解析會出錯:

  int dval;

  int Bear::mumble(int ival)

  {

    return  ival + dval;//----dval爲ZooAnimal私有成員,錯誤

  }

  解析過程如下:

  1. 在Bear類域中查找,沒有找到

  2. 在ZooAnimal類域中查找,找到,但是因爲私有成員不能訪問,編譯錯誤,不再向下查找。

  3. 因爲上一步,全局域中的名字就被屏蔽了。

多繼承中的情況

  多繼承中,名字的解析的時候,對父類域的查找是同時進行的。同時查找Root1類域和Root2類域,如果發現找到兩個同樣的名字,那麼由於二義性名字解析出錯,產生編譯錯誤。


========================更新========================

才現上次的研究還沒有抓住多繼承問題的重點,下面來一個新的總結:

1.    普通多繼承

類同時繼承自多個基類就是多繼承,C++支持多繼承。

但是需要注意多繼承產生的二意性問題,如果兩個基類都存在同名的成員,那麼在子類中對它的訪問就是有二義性錯誤的。名字解析過程中,同時找到兩個滿足條件的成員,這讓編譯器無法確定應該使用哪個。

其中比較典型的問題就是下面要講到的:菱形繼承問題。

2.    Private、public、protected繼承

2.1  private繼承

與public繼承的不同:

²  不是父類的子類型,也就是說不能賦值給父類指針或引用。

²  接口私有化。

通常多繼承時會使用public繼承a,private繼承b的方式。

可以通過如下方式還原private基類中的成員訪問級別:

public:

         //using private father func

         //You can level down the access level, butcan't level up it.

         using ZooAnimal::GetLegs;//correct

但是,經過我的測試,你可以還原或降低原有的訪問級別,不能提高訪問級別。比如基類中本身是private方法,在public塊中using這個方法是編譯錯誤的。

 

2.2  public繼承

普通的繼承方式

2.3  protected繼承

基類所有public成員成爲派生類的protected成員。

3.    菱形繼承

3.1 基本概念

菱形繼承是指:有基類A,B、C繼承自A,D多繼承自B、C,於是最終派生類D由兩條不同的繼承線繼承基類A。

這是多繼承中典型需要考慮的問題,這種情況下D的實例對基類A成員的訪問都是二義性錯誤的。

3.2 普通繼承方式中存在的問題

D對象中存在A類的2份不同的對象。

存儲2份A對象浪費空間。

A的構造函數會被調用2次。

引起二義性問題。

因爲是2份不同的對象,2個對象中成員的值不一致。

 

爲解決如上的種種問題,C++引用虛繼承。

4.    虛繼承

4.1  基本概念

虛繼承的基類叫做虛擬基類,無論它在派生層次中出現多少次,只有一個共享的基類子對象被繼承。

C++ Primmer書裏不建議大量使用虛繼承,會有效率問題。

4.2  構造、析構

虛繼承時,需要由最終派生類主動調用虛基類的構造函數,否則編譯錯誤。

因爲,對於虛繼承的情況編譯器會阻止中間派生類調用虛基類的構造函數。

比如:

在類D的實例初始過程中,D爲最終派生類,B、C爲中間派生類,A爲虛基類。

調用D的構造函數前,先找到它的基類B,調用B的構造函數,調用B構造函數前找到B的基類A,發現爲虛繼承關係,阻止B對A構造函數的調用。編譯器會確定D有沒有對A構造函數的調用,如沒有編譯報錯。

虛基類的構造函數最先被調用,然後再按聲明順序構造非虛基類。於是構造函數是:A、B、C、D調用順序。

析構函數是和構造相反的順序進行。

 

4.3  成員可視性

有如下幾種情況:

情況

普通繼承

虛繼承

A中的接口,未被B、C重載

二義性

OK,調用A的接口

B重載了A的接口,C未重載

二義性

OK,調用B的接口

B、C都重載的接口

二義性

二義性

 





  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章