C++之多態(tài)(內(nèi)容不錯(cuò))
編譯環(huán)境:WIN10 VS2017
這篇博客有點(diǎn)長(zhǎng),但都是滿滿的干貨,一定要看到最后,那才是重點(diǎn)。
什么是多態(tài)?
顧名思義就是同一個(gè)事物在不同場(chǎng)景下的多種形態(tài)。
下面會(huì)具體的詳細(xì)的介紹。
靜態(tài)多態(tài)
我們以前說(shuō)過(guò)的函數(shù)重載就是一個(gè)簡(jiǎn)單的靜態(tài)多態(tài)
int Add(int left, int right) { return left + right; } double Add(double left, int right) { return left + right; } int main() { Add(10, 20); //Add(10.0, 20.0); //這是一個(gè)問(wèn)題代碼 Add(10.0,20); //正常代碼 return 0; }
可以看出來(lái),靜態(tài)多態(tài)是編譯器在編譯期間完成的,編譯器會(huì)根據(jù)實(shí)參類型來(lái)選擇調(diào)用合適的函數(shù),如果有合適的函數(shù)可以調(diào)用就調(diào),沒(méi)有的話就會(huì)發(fā)出警告或者報(bào)錯(cuò)。。。比較簡(jiǎn)單,不做多介紹。
動(dòng)態(tài)多態(tài)
什么是動(dòng)態(tài)多態(tài)呢?
動(dòng)態(tài)多態(tài): 顯然這和靜態(tài)多態(tài)是一組反義詞,它是在程序運(yùn)行時(shí)根據(jù)基類的引用(指針)指向的對(duì)象來(lái)確定自己具體該調(diào)用哪一個(gè)類的虛函數(shù)。
我在西安臨潼上學(xué),我就以這邊的公交車舉個(gè)栗子?。?/p>
class TakeBus { public: void TakeBusToSubway() { cout << "go to Subway--->please take bus of 318" << endl; } void TakeBusToStation() { cout << "go to Station--->pelase Take Bus of 306 or 915" << endl; } }; //知道了去哪要做什么車可不行,我們還得知道有沒(méi)有這個(gè)車 class Bus { public: virtual void TakeBusToSomewhere(TakeBus& tb) = 0; //???為什么要等于0 }; class Subway:public Bus { public: virtual void TakeBusToSomewhere(TakeBus& tb) { tb.TakeBusToSubway(); } }; class Station :public Bus { public: virtual void TakeBusToSomewhere(TakeBus& tb) { tb.TakeBusToStation(); } }; int main() { TakeBus tb; Bus* b = NULL; //假設(shè)有十輛公交車,如果是奇數(shù)就是去地鐵口的,反之就是去火車站的 for (int i = 1; i <= 10; ++i) { if ((rand() % i) & 1) b = new Subway; else b = new Station; } b->TakeBusToSomewhere(tb); delete b; return 0; }
這就是一個(gè)簡(jiǎn)單的動(dòng)態(tài)多態(tài)的例子,它是在程序運(yùn)行時(shí)根據(jù)條件去選擇調(diào)用哪一個(gè)函數(shù)。
而且,從上面的例子我們還發(fā)現(xiàn)了我在每一個(gè)函數(shù)前都加了virtual這個(gè)虛擬關(guān)鍵字,想想為什么?如果不加會(huì)不會(huì)構(gòu)成多態(tài)呢?
干想不如上機(jī)實(shí)踐:
class Base { public: virtual void Funtest1(int i) { cout << "Base::Funtest1()" << endl; } void Funtest2(int i) { cout << "Base::Funtest2()" << endl; } }; class Drived :public Base { virtual void Funtest1(int i) { cout << "Drived::Fubtest1()" << endl; } virtual void Funtest2(int i) { cout << "Drived::Fubtest2()" << endl; } void Funtest2(int i) { cout << "Drived::Fubtest2()" << endl; } }; void TestVirtual(Base& b) { b.Funtest1(1); b.Funtest2(2); } int main() { Base b; Drived d; TestVirtual(b); TestVirtual(d); return 0; }
在調(diào)用FuncTest2的時(shí)候我們看出來(lái)他并沒(méi)有給我們調(diào)用派生類的函數(shù),因此我們可以對(duì)動(dòng)態(tài)多態(tài)的實(shí)現(xiàn)做個(gè)總結(jié)。
動(dòng)態(tài)多態(tài)的條件:
●基類中必須包含虛函數(shù),并且派生類中一定要對(duì)基類中的虛函數(shù)進(jìn)行重寫。
●通過(guò)基類對(duì)象的指針或者引用調(diào)用虛函數(shù)。
重寫 :
(a)基類中將被重寫的函數(shù)必須為虛函數(shù)(上面的檢測(cè)用例已經(jīng)證實(shí)過(guò)了)
(b)基類和派生類中虛函數(shù)的原型必須保持一致(返回值類型,函數(shù)名稱以及參數(shù)列表),協(xié)變和析構(gòu)函數(shù)(基類和派生類的析構(gòu)函數(shù)是不一樣的)除外
(c)訪問(wèn)限定符可以不同
那么問(wèn)題又來(lái)了,什么是協(xié)變?
協(xié)變:基類(或者派生類)的虛函數(shù)返回基類(派生類)的指針(引用)
總結(jié)一道面試題:那些函數(shù)不能定義為虛函數(shù)?
經(jīng)檢驗(yàn)下面的幾個(gè)函數(shù)都不能定義為虛函數(shù):
1)友元函數(shù),它不是類的成員函數(shù)
2)全局函數(shù)
3)靜態(tài)成員函數(shù),它沒(méi)有this指針
3)構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),以及賦值運(yùn)算符重載(可以但是一般不建議作為虛函數(shù))
抽象類:
在前面公交車的例子上我提了一個(gè)問(wèn)題:
class Bus { public: virtual void TakeBusToSomewhere(TakeBus& tb) = 0; //???為什么要等于0 };
在成員函數(shù)(必須為虛函數(shù))的形參列表后面寫上=0,則成員函數(shù)為純虛函數(shù)。包含純虛函數(shù)的類叫做抽象類(也叫接口類),抽象類不能實(shí)例化出對(duì)象。純虛函數(shù)在派生類中重新定義以后,派生類才能實(shí)例化出對(duì)象。純虛函數(shù)是一定要被繼承的,否則它存在沒(méi)有任何意義。
多態(tài)調(diào)用原理
class Base { public: virtual void Funtest1(int i) { cout << "Base::Funtest1()" << endl; } virtual void Funtest2(int i) { cout << "Base::Funtest2()" << endl; } int _data; }; int main() { cout << sizeof(Base) << endl; Base b; b._data = 10; return 0; }
8?不知道大家有沒(méi)有問(wèn)題,反正我是有疑惑了。以前在對(duì)象模型(https://blog.csdn.net/qq_39412582/article/details/80808754)時(shí)我提到過(guò)怎么來(lái)求一個(gè)類的大小。按照那個(gè)方法,這里應(yīng)該是4才對(duì)啊,為什么會(huì)是8呢?
通過(guò)觀察。我們發(fā)現(xiàn)這個(gè)例子里面和以前不一樣,類成員函數(shù)變成了虛函數(shù),這是不是引起類大小變化的原因呢?
我們假設(shè)就是這樣,然后看看內(nèi)存里是怎么存儲(chǔ)的呢?
可以看到它在內(nèi)存里多了四個(gè)字節(jié),那這四個(gè)字節(jié)的內(nèi)容到底是什么呢?
是不是有點(diǎn)看不懂,我們假設(shè)它是一個(gè)地址去看地址里存的東西的時(shí)候發(fā)現(xiàn)它存的是兩個(gè)地址。
我假設(shè)它是虛函數(shù)的地址,我們來(lái)驗(yàn)證一下:
typedef void (__stdcall *PVFT)(); //函數(shù)指針 int main() { cout << sizeof(Base) << endl; Base b; b._data = 10; PVFT* pVFT = (PVFT*)(*((int*)&b)); while (*pVFT) { (*pVFT)(); pVFT+=1; } return 0; }
結(jié)果好像和我們的猜想一樣,是一件開心的事。然后我給一張圖總結(jié)一下:
在反匯編中我們還可以看到,如果含有虛函數(shù)的類中沒(méi)有定義構(gòu)造函數(shù),編譯器會(huì)自動(dòng)合成一個(gè)構(gòu)造函數(shù)
對(duì)于派生類的東西我給個(gè)鏈接仔細(xì)看,人家總結(jié)的超級(jí)贊,我偷個(gè)懶就不寫了,老鐵們包容下啊。
派生類虛表:
1.先將基類的虛表中的內(nèi)容拷貝一份
2.如果派生類對(duì)基類中的虛函數(shù)進(jìn)行重寫,使用派生類的虛函數(shù)替換相同偏移量位置的基類虛函數(shù)
3.如果派生類中新增加自己的虛函數(shù),按照其在派生類中的聲明次序,放在上述虛函數(shù)之后
https://coolshell.cn/articles/12176.html
多態(tài)缺陷
●降低了程序運(yùn)行效率(多態(tài)需要去找虛表的地址)
●空間浪費(fèi)
- C++中的封裝、繼承、多態(tài)理解
- 詳解C++ 多態(tài)的實(shí)現(xiàn)及原理
- c++語(yǔ)言中虛函數(shù)實(shí)現(xiàn)多態(tài)的原理詳解
- C++中繼承與多態(tài)的基礎(chǔ)虛函數(shù)類詳解
- C++面向?qū)ο笾鄳B(tài)的實(shí)現(xiàn)和應(yīng)用詳解
- C語(yǔ)言實(shí)現(xiàn)C++繼承和多態(tài)的代碼分享
- C語(yǔ)言模式實(shí)現(xiàn)C++繼承和多態(tài)的實(shí)例代碼
- C++多繼承多態(tài)的實(shí)例詳解
- C++ 通過(guò)指針實(shí)現(xiàn)多態(tài)實(shí)例詳解
- C++中的多態(tài)與虛函數(shù)的內(nèi)部實(shí)現(xiàn)方法
- C++多態(tài)的實(shí)現(xiàn)機(jī)制深入理解
- 詳解C++編程的多態(tài)性概念
- C++多態(tài)的實(shí)現(xiàn)及原理詳細(xì)解析
- 深入解析C++中的虛函數(shù)與多態(tài)
- 深入理解C++的多態(tài)性
相關(guān)文章
詳解C語(yǔ)言數(shù)組靈活多變的訪問(wèn)形式
這篇文章主要介紹了詳解C語(yǔ)言數(shù)組靈活多變的訪問(wèn)形式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01關(guān)于C/C++中可變參數(shù)的詳細(xì)介紹(va_list,va_start,va_arg,va_end)
可變參數(shù)的函數(shù)原理其實(shí)很簡(jiǎn)單,而va系列是以宏定義來(lái)定義的,實(shí)現(xiàn)跟堆棧相關(guān).我們寫一個(gè)可變函數(shù)的C函數(shù)時(shí),有利也有弊,所以在不必要的場(chǎng)合,我們無(wú)需用到可變參數(shù)。如果在C++里,我們應(yīng)該利用C++的多態(tài)性來(lái)實(shí)現(xiàn)可變參數(shù)的功能,盡量避免用C語(yǔ)言的方式來(lái)實(shí)現(xiàn)2013-10-10用C語(yǔ)言的泛型實(shí)現(xiàn)交換兩個(gè)變量值
在日常編程里面經(jīng)常會(huì)遇到交換兩個(gè)變量的內(nèi)容的任務(wù),對(duì)于泛型類型而言有兩種泛型策略來(lái)實(shí)現(xiàn),下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)。2016-08-08c++動(dòng)態(tài)庫(kù)調(diào)用的實(shí)現(xiàn)
本文主要介紹了c++動(dòng)態(tài)庫(kù)調(diào)用的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07c語(yǔ)言結(jié)構(gòu)體字節(jié)對(duì)齊的實(shí)現(xiàn)方法
在c語(yǔ)言的結(jié)構(gòu)體里面一般會(huì)按照某種規(guī)則去進(jìn)行字節(jié)對(duì)齊。本文就來(lái)介紹一下如何實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解下2021-07-07C語(yǔ)言中的運(yùn)算符和結(jié)合性問(wèn)題
這篇文章主要介紹了C語(yǔ)言中的運(yùn)算符和結(jié)合性問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03C++實(shí)現(xiàn)LeetCode(199.二叉樹的右側(cè)視圖)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(199.二叉樹的右側(cè)視圖),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08