您好,登錄后才能下訂單哦!
小編給大家分享一下C++之多態(tài)的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
什么是多態(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)型來(lái)選擇調(diào)用合適的函數(shù),如果有合適的函數(shù)可以調(diào)用就調(diào),沒(méi)有的話(huà)就會(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ù)基類(lèi)的引用(指針)指向的對(duì)象來(lái)確定自己具體該調(diào)用哪一個(gè)類(lèi)的虛函數(shù)。
我在西安臨潼上學(xué),我就以這邊的公交車(chē)舉個(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; } }; //知道了去哪要做什么車(chē)可不行,我們還得知道有沒(méi)有這個(gè)車(chē) 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è)有十輛公交車(chē),如果是奇數(shù)就是去地鐵口的,反之就是去火車(chē)站的 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)用派生類(lèi)的函數(shù),因此我們可以對(duì)動(dòng)態(tài)多態(tài)的實(shí)現(xiàn)做個(gè)總結(jié)。
動(dòng)態(tài)多態(tài)的條件:
●基類(lèi)中必須包含虛函數(shù),并且派生類(lèi)中一定要對(duì)基類(lèi)中的虛函數(shù)進(jìn)行重寫(xiě)。
●通過(guò)基類(lèi)對(duì)象的指針或者引用調(diào)用虛函數(shù)。
重寫(xiě) :
(a)基類(lèi)中將被重寫(xiě)的函數(shù)必須為虛函數(shù)(上面的檢測(cè)用例已經(jīng)證實(shí)過(guò)了)
(b)基類(lèi)和派生類(lèi)中虛函數(shù)的原型必須保持一致(返回值類(lèi)型,函數(shù)名稱(chēng)以及參數(shù)列表),協(xié)變和析構(gòu)函數(shù)(基類(lèi)和派生類(lèi)的析構(gòu)函數(shù)是不一樣的)除外
(c)訪(fǎng)問(wèn)限定符可以不同
那么問(wèn)題又來(lái)了,什么是協(xié)變?
協(xié)變:基類(lèi)(或者派生類(lèi))的虛函數(shù)返回基類(lèi)(派生類(lèi))的指針(引用)
總結(jié)一道面試題:那些函數(shù)不能定義為虛函數(shù)?
經(jīng)檢驗(yàn)下面的幾個(gè)函數(shù)都不能定義為虛函數(shù):
1)友元函數(shù),它不是類(lèi)的成員函數(shù)
2)全局函數(shù)
3)靜態(tài)成員函數(shù),它沒(méi)有this指針
3)構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),以及賦值運(yùn)算符重載(可以但是一般不建議作為虛函數(shù))
抽象類(lèi):
在前面公交車(chē)的例子上我提了一個(gè)問(wèn)題:
class Bus { public: virtual void TakeBusToSomewhere(TakeBus& tb) = 0; //???為什么要等于0 };
在成員函數(shù)(必須為虛函數(shù))的形參列表后面寫(xiě)上=0,則成員函數(shù)為純虛函數(shù)。包含純虛函數(shù)的類(lèi)叫做抽象類(lèi)(也叫接口類(lèi)),抽象類(lèi)不能實(shí)例化出對(duì)象。純虛函數(shù)在派生類(lèi)中重新定義以后,派生類(lèi)才能實(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è)類(lèi)的大小。按照那個(gè)方法,這里應(yīng)該是4才對(duì)啊,為什么會(huì)是8呢?
通過(guò)觀察。我們發(fā)現(xiàn)這個(gè)例子里面和以前不一樣,類(lèi)成員函數(shù)變成了虛函數(shù),這是不是引起類(lèi)大小變化的原因呢?
我們假設(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é)果好像和我們的猜想一樣,是一件開(kāi)心的事。然后我給一張圖總結(jié)一下:
在反匯編中我們還可以看到,如果含有虛函數(shù)的類(lèi)中沒(méi)有定義構(gòu)造函數(shù),編譯器會(huì)自動(dòng)合成一個(gè)構(gòu)造函數(shù)
對(duì)于派生類(lèi)的東西我給個(gè)鏈接仔細(xì)看,人家總結(jié)的超級(jí)贊,我偷個(gè)懶就不寫(xiě)了,老鐵們包容下啊。
派生類(lèi)虛表:
1.先將基類(lèi)的虛表中的內(nèi)容拷貝一份
2.如果派生類(lèi)對(duì)基類(lèi)中的虛函數(shù)進(jìn)行重寫(xiě),使用派生類(lèi)的虛函數(shù)替換相同偏移量位置的基類(lèi)虛函數(shù)
3.如果派生類(lèi)中新增加自己的虛函數(shù),按照其在派生類(lèi)中的聲明次序,放在上述虛函數(shù)之后
https://coolshell.cn/articles/12176.html
多態(tài)缺陷
●降低了程序運(yùn)行效率(多態(tài)需要去找虛表的地址)
●空間浪費(fèi)
以上是“C++之多態(tài)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。