4.6.7多繼承語法
C++中允許一個(gè)類繼承多個(gè)類
多繼承可能會(huì)引發(fā)父類中有同名成員出現(xiàn),需要加作用域區(qū)分
C++實(shí)際開發(fā)中不建議使用多繼承
4.6.8菱形繼承--虛繼承
概念:兩個(gè)派生類繼承同一個(gè)基類,又有某個(gè)類同時(shí)繼承這兩個(gè)派生類,
這種繼承稱為菱形繼承,或鉆石繼承;
虛繼承:在繼承之前加virtual關(guān)鍵字
虛基類:被虛繼承的類稱為虛基類
虛基類指針 vbptr (virtual base pointer) :虛繼承基類的派生類的對(duì)象內(nèi)存中會(huì)生成一個(gè)vbptr
指向 vbtable 虛基類表 :虛基類表中記錄了指針到相同屬性數(shù)據(jù)的偏移量,
通過偏移量找到數(shù)據(jù),所以同一屬性數(shù)據(jù)不需要重復(fù)存儲(chǔ).
示例:
class Animal
{
public:
int m_Age;
};
//利用虛繼承 解決菱形繼承問題
//在繼承之前加關(guān)鍵字 virtual
//Animal類稱為 虛基類
class Sheep:virtual public Animal{};
class Tuo:virtual public Animal{};
class SheepTuo:public Sheep, public Tuo{};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//當(dāng)菱形繼承,兩個(gè)父類擁有相同的屬性,需要加以作用域區(qū)分
cout << "st.Sheep::m_Age=" << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl;
//這份屬性數(shù)據(jù)只需一份,菱形繼承導(dǎo)致數(shù)據(jù)有兩份,資源浪費(fèi)
cout << "st.m_Age=" << st.m_Age << endl;
}
總結(jié): 1.菱形繼承帶來的主要問題是子類繼承兩份相同的數(shù)據(jù),導(dǎo)致資源浪費(fèi)以及毫無意義
? ? ? 2.利用虛繼承可以解決菱形繼承問題
4.7多態(tài)
4.7.1多態(tài)的基本概念
多態(tài)是C++面向?qū)ο笕筇匦灾?/p>
多態(tài)分為兩類:
1.靜態(tài)多態(tài):函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài),復(fù)用函數(shù)名;
2.動(dòng)態(tài)多態(tài):派生類和虛函數(shù)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài);
區(qū)別:
1.靜態(tài)多態(tài)的函數(shù)地址早綁定,編譯階段確定函數(shù)地址;
2.動(dòng)態(tài)多態(tài)的函數(shù)地址晚綁定,運(yùn)行階段確定函數(shù)地址;
示例:
class Animal
{
public:
void speak1()
{
cout << "Animal::speak1()" << endl;
}
//虛函數(shù)
virtual void speak2()
{
cout << "Animal::speak2()" << endl;
}
};
class Cat
:public Animal
{
public:
void speak1()
{
cout << "Cat::speak1()" << endl;
}
//重寫 函數(shù)返回值類型 函數(shù)名 參數(shù)列表 完全相同
void speak2()
{
cout << "Cat::speak2()" << endl;
}
};
void doSpeak(Animal &animal)
{
//地址早綁定 在編譯階段確定函數(shù)地址
? ? //如果想執(zhí)行讓貓說話,那么這個(gè)函數(shù)地址不能提前綁定
? ? //需要在運(yùn)行階段進(jìn)行綁定,地址晚綁定,即實(shí)現(xiàn)動(dòng)態(tài)多態(tài)
animal.speak1();
cout <<"-----------" << endl;
//函數(shù)地址不能提前確定
//需要看傳入對(duì)象類型
animal.speak2();
//動(dòng)態(tài)多態(tài)滿足條件
//1.有繼承關(guān)系
//2.子類要重寫父類的虛函數(shù)
//動(dòng)態(tài)多態(tài)使用
//父類的指針或引用指向子類對(duì)象
}
void test01()
{
Cat cat;
doSpeak(cat);//Animal &animal=cat;
}
總結(jié):
動(dòng)態(tài)多態(tài)滿足條件
1.有繼承關(guān)系
2.子類要重寫父類的虛函數(shù)
動(dòng)態(tài)多態(tài)使用:父類的指針或引用指向子類對(duì)象
重寫 函數(shù)返回值類型 函數(shù)名 參數(shù)列表 完全相同
4.7.2多態(tài)案例1-計(jì)算器類
案例描述:
分別利用普通寫法和多態(tài)技術(shù),設(shè)計(jì)實(shí)現(xiàn)兩個(gè)操作數(shù)進(jìn)行計(jì)算的計(jì)算器類
多態(tài)優(yōu)點(diǎn):
1.代碼組織結(jié)構(gòu)清晰
2.可讀性強(qiáng)
3.利于前期和后期和擴(kuò)展以及維護(hù)
4.7.3純虛函數(shù)和抽象類
在多態(tài)中,通常父類中虛函數(shù)的實(shí)現(xiàn)是毫無意義的,主要都是調(diào)用子類重寫的內(nèi)容
因此可以將虛函數(shù)改為純虛函數(shù).
純虛函數(shù)語法:
virtual 返回值類型 函數(shù)名 (參數(shù)列表)=0
當(dāng)類中有了純虛函數(shù),這個(gè)類稱為抽象類.
抽象類特點(diǎn):
1.無法實(shí)例化對(duì)象;
2.子類必須重寫抽象類中的純虛函數(shù),否則該子類也是抽象類;
4.7.4多態(tài)案例2-制作飲品
案例描述:
制作飲品大致流程為 煮水 沖泡 倒入杯中 加入輔料
利用多態(tài)技術(shù)實(shí)現(xiàn)本案例,提供抽象制作飲品基類,提供子類制作咖啡和茶葉
4.7.5虛析構(gòu)和純虛析構(gòu)
多態(tài)使用時(shí),如果子類中屬性開辟到堆區(qū),那么父類指針在釋放時(shí)無法調(diào)用到子類的析構(gòu)代碼
解決方式:將父類中的析構(gòu)函數(shù)改為虛析構(gòu)或純虛析構(gòu)
虛析構(gòu)和純虛析構(gòu)共性:
1.可以解決父類指針釋放子類對(duì)象;
2.都需要有具體的函數(shù)實(shí)現(xiàn);
虛析構(gòu)和純虛析構(gòu)區(qū)別:
如果是純虛析構(gòu),該類屬于抽象類,無法實(shí)例化對(duì)象
總結(jié):
1.虛析構(gòu)或純虛析構(gòu)就是用來解決通過父類指針釋放子類對(duì)象;
2.如果子類中沒有堆區(qū)數(shù)據(jù),可以不寫為虛析構(gòu)或純虛析構(gòu);
3.擁有純虛析構(gòu)函數(shù)的類也屬于抽象類;