C 多重繼承 虛繼承

2021-06-26 08:50:39 字數 3473 閱讀 6314

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);這個呼叫也會產生二義性錯誤,編譯器不知道應該呼叫哪個介面。

名字查詢過程

查詢過程是乙個域乙個域的查詢,直到全域性域。每個類都有乙個類域。看如下示例**:

[cpp]view plain

copy

class

zooanimal ;  

class

bear : 

public

zooanimal ;  

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)

而如下名字解析會出錯:

int dval;

int bear::mumble(int ival)

解析過程如下:

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都過載的介面

二義性二義性

c 多重繼承 虛繼承 菱形繼承

多重繼承的特性和使用和單繼承沒區別。按照單繼承來使用即可。多重繼承可能會引入乙個 菱形繼承的問題 這個問題其實並不算問題,因為它合情合理,即多個基類 擁有乙個共同的基類,那麼在構造的時候,由於構造的遞迴特性,就會出現共同基類出現兩份例項的 情況,而且如果針對性修改,那麼如果不在訪問共同基類的時候先指...

C 多重繼承與虛繼承

在派生類中對基類成員的訪問應該是唯一的。但是,在多繼承情況下,可能造成對基類中某個成員的訪問出現了不一致的情況,這時就稱對基類成員的訪問產生了二義性。派生類在訪問基類成員函式時,由於基類存在同名的成員函式,導致無法確定訪問的是哪個基類的成員函式,因此出現了二義性錯誤。例如 includeusing ...

c 多重繼承和虛繼承

多重繼承 多重繼承是指從多個直接基類中產生派生類的能力。多重繼承的派生類繼承了所有父類的屬性。struct base1 struct base2 struct d1 public base1,public base2 d1 default 虛繼承 虛繼承的目的是令某個類做出宣告,承諾願意共享它的基類...