本文主要是討論類的多繼承,多繼承的方式可使派生類具有多個基類的特性。以下是乙個繼承關係,c從a和b基類中派生出來,在派生類c中,包含了基類a與基類b的成員,還有c類自己的成員。
在上述關係圖中,可以看到,類繼承的結構很清晰,但在很多時候,有可能有以下繼承關係:
在這幅類的繼承關係圖中可以看到,b1和b2分別繼承於a,再由b1和b2派生出c,這關係看起來很簡單,但在內部,有點麻煩?
產生二義性。
a派生出b1和b2,所以b1和b2中分別包含a的成員,再由b1和b2派生出c,此時c包含了b1和b2類的成員,所以c中總共有2個a成員。這時候麻煩了,如何要對c類中b1父類中的a類成員修改,或者是對c類中的b2父類中的a類成員修改呢?這有點繞口,c++提供了一種方法是通過作用域來指定,可以參考前一篇博文《c++學習-繼承中的作用域(10)》
這裡給乙個簡單的例子:
#include using namespace std;
class a
int ma;
};class b1 : public a
int mb1;
};class b2 : public a
int mb2;
};class c : public b1, public b2
void print() const
int mc;
};int main()
執行結果:
b1::ma = 30
b2::ma = 50
mb1 = 20
mb2 = 40
mc = 10
從這個例子可以看到,使用作用域運算子"::"來消除二義性,這樣就能巧妙地避免那個棘手的問題。不過在類c中,包含了2個a的部分,有點浪費空間。當然c++是非常偉大的,還有另一種更好的辦法,就是使用虛基類,也可以稱為虛繼承。
虛基類的作用:使用虛基類能夠在多重派生的過程中,使公有的基類在派生類中只有乙個拷貝,這樣就能解決上述二義性的錯誤。如果使用虛基類,那麼以上類c當中也就只有1個a的部分。在虛基類的使用過程中,還有乙個非常重要的特性,看例子便可發現。
看例子:
#include using namespace std;
class a
int ma;
};class b1 : virtual public a
int mb1;
};class b2 : virtual public a
int mb2;
};class c : public b1, public b2
void print() const
int mc;
};int main()
執行結果:
b1::ma = 0
b2::ma = 0
ma = 0
mb1 = 20
mb2 = 40
mc = 10
b1::ma = 163
b2::ma = 163
ma = 163
mb1 = 20
mb2 = 40
mc = 10
對比一下這兩個例子,差別很小,將作用域的呼叫方式給去掉了。在這個例子當中,使用了virtual方式繼承了基類a,所以在b1和b2中,對類a只有乙個拷貝,若使用virtual方式繼承,不管派生多少派生類,類a永遠只有乙個拷貝。對ma進行修改,從b1或b2呼叫ma,都是呼叫同乙個副本,從結果可以看出這一結論。
重要:
虛基類的建構函式的呼叫方法與一般基類的建構函式的呼叫方法是不同的。在這個例子中,編譯器沒有呼叫b1或者b2的建構函式來呼叫基類a的建構函式,因為在虛繼承過程中,基類a只有乙個拷貝,所以編譯器無法確定應該由類b1或者類b2的建構函式來呼叫基類a的建構函式,所以此時呼叫的是基類a的預設建構函式,所以剛開始ma的結果為0,是基類a的預設建構函式設定的預設值。
c++規定,由虛基類經過一次或者多次派生出來的派生類,在其每乙個派生類的建構函式的成員初始化列表中必須給出對虛基類的建構函式的呼叫,如果未列出,則呼叫虛基類的預設建構函式。
在本例當中,在執行b1和b2的建構函式時都不呼叫虛基類a的建構函式,而是在類c中的建構函式直接呼叫虛基類a的預設建構函式。(因為編譯器無法確定)
再看乙個與上面幾乎是一樣的例子,只有乙個區別,就是在類c的初始化列表中顯示的呼叫虛基類a的建構函式,如下:
#include using namespace std;
class a
int ma;
};class b1 : virtual public a
int mb1;
};class b2 : virtual public a
int mb2;
};class c : public b1, public b2
void print() const
int mc;
};int main()
執行結果:
b1::ma = 20
b2::ma = 20
ma = 20
mb1 = 20
mb2 = 40
mc = 10
b1::ma = 163
b2::ma = 163
ma = 163
mb1 = 20
mb2 = 40
mc = 10
唯一的區別就是這行**:
c(int x, int a, int b, int c, int d) : a(a), b1(a, b), b2(c, d), mc(x) {}
在這裡顯示地呼叫虛基類a的建構函式,並傳入初始值,所以第一次列印ma的值不是0,而是20。
虛基類有這些特性,務必記住,看例子便可知分曉,over!
多繼承和虛基類
一.多繼承機制存在哪些問題,怎麼解決這些問題?歧義性 相同名稱的成員在記憶體中同時擁有多個拷貝,當通過派生類物件使用這些成員時,就會產生歧義性。作用域操作符雖然能解決歧義性問題,但並沒有解決多個拷貝的問題。類d 的記憶體布局 使用虛擬機制注意事項 1.若在虛基類中定義了帶引數的建構函式,而沒有定義預...
C 多繼承與虛基類
多繼承的定義 派生類的基類大於乙個 語法 class 派生類名 繼承方式1 基類名1,繼承方式2 基類名2.多重繼承與建構函式的關係 多重繼承時建構函式的作用 1 初始化派生類 自己 2 呼叫該派生類所有基類建構函式,並且為所有基類傳參 引數個數必須包含所有基類所需引數 建構函式語法 派生類建構函式...
39多繼承和虛基類
多繼承 horse類 bird類 pegasus類 飛馬 多繼承帶來的問題 二義性問題 繼承了兩個以上重名的函式,使得c 不知道該呼叫哪乙個函式 解決方式 寫上基類名稱 菱形繼承 horse和bird都有共同的基類animal 導致在建立pegasus類物件時會呼叫兩次animal類,產生兩次繼承。...