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 虛繼承 虛繼承的目的是令某個類做出宣告,承諾願意共享它的基類...