多重繼承與虛繼承
- 1.單繼承和多重繼承的區別
- 2.語法規則
- 示例代碼:多重繼承子類指定父類的構造
- 示例代碼:多重繼承子類隱藏父類的同名方法
- 3.虛繼承解決多重繼承遇到的bug
- 示例代碼:環狀繼承引發的問題
- 3.1 虛基類:
- 3.2 語法規則:
- 3.3 總結:普通繼承跟虛繼承的區別
- 3.4 虛基類表:
- 示例代碼:虛繼承跟普通繼承(沒有使用virtual)的區別
1.單繼承和多重繼承的區別
單繼承:子類只有一個父類
多重繼承:一個類如果具備了多個其它類的特點,就可以使用多重繼承
情況一:子類繼承了多個父類
圓桌: 繼承圓和桌子
騾子: 繼承馬和驢
豹子: 繼承貓科動物和哺乳動物
情況二:A派生出B,B派生出C C接著派生(重點)
直接父類:Cat就是波斯貓的直接父類
間接父類:Animal就是波斯貓的間接父類
Animal --》Cat --》波斯貓 --》其他貓
環狀繼承(菱形繼承)
A動物
哺乳B C貓科
D豹子
產生的問題:
問題一:A被構建了多次,浪費了存儲空間(希望A只構建一次)
問題二:二義性問題,通過D去調用A里面的方法有兩條途徑(一條通過B調用,還有一條通過C調用)
2.語法規則
class 子類的名字:public 父類1,public 父類2 //公有繼承
{}
- 子類大小: 所有父類大小之和+子類本身的大小,滿足字節對齊
- 構造函數調用順序:多個父類從左到右調用
- 析構函數調用順序:多個父類從右到左調用
- 指定調用父類的構造函數
roundtable(int newr, int w,int h):circle(newr),desk(w,h)
{}
注意:指定的時候多個父類的順序無所謂,最終父類構造函數調用的順序以當初繼承時候書寫的左右順序為準
示例代碼:多重繼承子類指定父類的構造
#include <iostream>
#include <cstring>
using namespace std;/*多重繼承子類指定父類的構造*/
class Catamount //貓科動物
{
public:Catamount(){cout<<"貓科構造"<<endl;}Catamount(int n){cout<<"貓科構造,帶int參數: "<<n<<endl;}~Catamount(){cout<<"貓科析構"<<endl;}
};class Mammal //哺乳動物
{
public:Mammal(){cout<<"哺乳構造"<<endl;}Mammal(string name){cout<<"哺乳構造,帶string的參數: "<<name<<endl;}~Mammal(){cout<<"哺乳析構"<<endl;}
};class Leopard:public Mammal,public Catamount
{
public://情況1:部分指定,指定了哺乳動物,沒有指定的依然使用無參構造// Leopard():Mammal("草")// Leopard():Catamount(666)//情況2:完全指定// 完全指定的時候,指定父類構造函數的順序無所謂Leopard():Catamount(888),Mammal("肉")// Leopard():Mammal("肉"),Catamount(888){cout<<"豹子構造"<<endl;}~Leopard(){cout<<"豹子析構"<<endl;}
};int main(int argc,char **argv)
{Leopard l1;return 0;
}/*
輸出結果:
情況1:哺乳構造,帶string的參數: 草 / 哺乳構造貓科構造 / 貓科構造,帶int參數: 666豹子構造 / 豹子構造豹子析構 / 豹子析構貓科析構 / 貓科析構哺乳析構 / 哺乳析構
情況2:哺乳構造,帶string的參數: 肉貓科構造,帶int參數: 888豹子構造豹子析構貓科析構哺乳析構
*/
- 子類出現跟父類同名的方法(子類隱藏了父類的同名方法)
round.getarea(); //圓桌自己的getarea
round.Circle::getarea(); //圓桌對象調用父類Circle的getarea
round.Desk::getarea(); //圓桌對象調用父類Desk的getarea
示例代碼:多重繼承子類隱藏父類的同名方法
#include <iostream>
#include <cstring>
using namespace std;/*多重繼承子類隱藏父類的同名方法
*/
class Catamount //貓科動物
{
public:void eat(){cout<<"貓科動物吃"<<endl;}
};class Mammal //哺乳動物
{
public:void eat(){cout<<"哺乳動物吃"<<endl;}
};class Leopard:public Mammal,public Catamount
{
public:void eat(){cout<<"豹子吃"<<endl;}
};int main(int argc,char **argv)
{Leopard l1;l1.eat(); //兩個父類的eat都被隱藏//調用父類的eatl1.Mammal::eat();l1.Catamount::eat();return 0;
}/*
執行結果:豹子吃哺乳動物吃貓科動物吃
*/
3.虛繼承解決多重繼承遇到的bug
示例代碼:環狀繼承引發的問題
#include <iostream>
#include <cstring>
using namespace std;/*環狀繼承引發的問題?問題1:Animal被構建多次浪費了存儲空間,C++希望Animal只構建一次問題2:豹子調用eat方法提示二義性*/
class Animal
{
public:Animal(){cout<<"Animal無參構造了"<<endl;}void eat(){cout<<"Animal eat"<<endl;}
};class Catamount:public Animal //貓科動物
{
public:Catamount(){cout<<"Catamount無參構造"<<endl;}
};class Mammal:public Animal //哺乳動物
{
public:Mammal(){cout<<"Mammal無參構造"<<endl;}
};class Leopard:public Mammal,public Catamount
{
public:Leopard(){cout<<"Leopard無參構造"<<endl;}
};int main(int argc,char **argv)
{Leopard l1;//編譯報錯:提示eat有二義性// l1.eat(); // error: request for member ‘eat’ is ambiguous//解決問題2:但是無法解決問題1l1.Mammal::eat();l1.Catamount::eat();return 0;
}/*
執行結果:
問題1: 可以看出Animal類被創建的2次Animal無參構造了Mammal無參構造Animal無參構造了Catamount無參構造Leopard無參構造問題2: 可以解決報錯,但無法解決問題1Animal無參構造了Mammal無參構造Animal無參構造了Catamount無參構造Leopard無參構造Animal eatAnimal eat
*/
3.1 虛基類:
\quad 虛基類使得從多個類(B和C)它們的基類相同,共同的虛基類是(A)派生出(D)的對象只繼承一個基類對象(通俗的講就是子類使用virtual繼承了父類,這個父類就是虛基類)
3.2 語法規則:
class 子類:virtual public 父類
class 子類:public virtual 父類
{};
virtual和public的次序無關緊要
3.3 總結:普通繼承跟虛繼承的區別
- 虛繼承可以解決二義性和A被構建多次這兩個問題,普通繼承不能解決;虛繼承通過增加一個指針(浪費了一點存儲空間),換取了更高的效率
- 只要一個類虛繼承了其它類,那么該類所有的對象中都會新增一個指針,該指針專門用來指向系統中
虛基類表的首地址
3.4 虛基類表:
C++中專門用來存放虛基類地址的一種數據結構
底層原理如下:
示例代碼:虛繼承跟普通繼承(沒有使用virtual)的區別
#include <iostream>
#include <cstring>
using namespace std;/*虛繼承的底層原理(虛繼承跟普通繼承(沒有使用virtual)的區別):1.如果一個子類虛繼承了另外一個父類,子類的地址空間中會多出一個指針2.指針的作用:用來指向虛基類表的首地址
*/
class Animal
{
public:Animal(){cout<<"Animal無參構造了"<<endl;}void eat(){cout<<"Animal eat"<<endl;}
};class Catamount:virtual public Animal //貓科動物
{
public:Catamount(){cout<<"Catamount無參構造"<<endl;}private:int m_age;
};class Mammal:virtual public Animal //哺乳動物
{
public:Mammal(){cout<<"Mammal無參構造"<<endl;}
};class Leopard:public Mammal,public Catamount
{
public:Leopard(){cout<<"Leopard無參構造"<<endl;}
};int main(int argc,char **argv)
{cout<<"貓科類的大小:"<<sizeof(Catamount)<<endl; // 貓科類的大小:16cout<<"哺乳類的大小:"<<sizeof(Mammal)<<endl; // 哺乳類的大小:8return 0;
}/*
執行結果:Leopard l1;l1.eat(); 時,輸出:Animal無參構造了Mammal無參構造Catamount無參構造Leopard無參構造Animal eat
*/