您好,登錄后才能下訂單哦!
小編給大家分享一下C++之多態的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
什么是多態?
顧名思義就是同一個事物在不同場景下的多種形態。
下面會具體的詳細的介紹。
靜態多態
我們以前說過的函數重載就是一個簡單的靜態多態
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); //這是一個問題代碼 Add(10.0,20); //正常代碼 return 0; }
可以看出來,靜態多態是編譯器在編譯期間完成的,編譯器會根據實參類型來選擇調用合適的函數,如果有合適的函數可以調用就調,沒有的話就會發出警告或者報錯。。。比較簡單,不做多介紹。
動態多態
什么是動態多態呢?
動態多態: 顯然這和靜態多態是一組反義詞,它是在程序運行時根據基類的引用(指針)指向的對象來確定自己具體該調用哪一個類的虛函數。
我在西安臨潼上學,我就以這邊的公交車舉個栗子啊:
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; } }; //知道了去哪要做什么車可不行,我們還得知道有沒有這個車 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; //假設有十輛公交車,如果是奇數就是去地鐵口的,反之就是去火車站的 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; }
這就是一個簡單的動態多態的例子,它是在程序運行時根據條件去選擇調用哪一個函數。
而且,從上面的例子我們還發現了我在每一個函數前都加了virtual這個虛擬關鍵字,想想為什么?如果不加會不會構成多態呢?
干想不如上機實踐:
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; }
在調用FuncTest2的時候我們看出來他并沒有給我們調用派生類的函數,因此我們可以對動態多態的實現做個總結。
動態多態的條件:
●基類中必須包含虛函數,并且派生類中一定要對基類中的虛函數進行重寫。
●通過基類對象的指針或者引用調用虛函數。
重寫 :
(a)基類中將被重寫的函數必須為虛函數(上面的檢測用例已經證實過了)
(b)基類和派生類中虛函數的原型必須保持一致(返回值類型,函數名稱以及參數列表),協變和析構函數(基類和派生類的析構函數是不一樣的)除外
(c)訪問限定符可以不同
那么問題又來了,什么是協變?
協變:基類(或者派生類)的虛函數返回基類(派生類)的指針(引用)
總結一道面試題:那些函數不能定義為虛函數?
經檢驗下面的幾個函數都不能定義為虛函數:
1)友元函數,它不是類的成員函數
2)全局函數
3)靜態成員函數,它沒有this指針
3)構造函數,拷貝構造函數,以及賦值運算符重載(可以但是一般不建議作為虛函數)
抽象類:
在前面公交車的例子上我提了一個問題:
class Bus { public: virtual void TakeBusToSomewhere(TakeBus& tb) = 0; //???為什么要等于0 };
在成員函數(必須為虛函數)的形參列表后面寫上=0,則成員函數為純虛函數。包含純虛函數的類叫做抽象類(也叫接口類),抽象類不能實例化出對象。純虛函數在派生類中重新定義以后,派生類才能實例化出對象。純虛函數是一定要被繼承的,否則它存在沒有任何意義。
多態調用原理
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?不知道大家有沒有問題,反正我是有疑惑了。以前在對象模型(https://blog.csdn.net/qq_39412582/article/details/80808754)時我提到過怎么來求一個類的大小。按照那個方法,這里應該是4才對啊,為什么會是8呢?
通過觀察。我們發現這個例子里面和以前不一樣,類成員函數變成了虛函數,這是不是引起類大小變化的原因呢?
我們假設就是這樣,然后看看內存里是怎么存儲的呢?
可以看到它在內存里多了四個字節,那這四個字節的內容到底是什么呢?
是不是有點看不懂,我們假設它是一個地址去看地址里存的東西的時候發現它存的是兩個地址。
我假設它是虛函數的地址,我們來驗證一下:
typedef void (__stdcall *PVFT)(); //函數指針 int main() { cout << sizeof(Base) << endl; Base b; b._data = 10; PVFT* pVFT = (PVFT*)(*((int*)&b)); while (*pVFT) { (*pVFT)(); pVFT+=1; } return 0; }
結果好像和我們的猜想一樣,是一件開心的事。然后我給一張圖總結一下:
在反匯編中我們還可以看到,如果含有虛函數的類中沒有定義構造函數,編譯器會自動合成一個構造函數
對于派生類的東西我給個鏈接仔細看,人家總結的超級贊,我偷個懶就不寫了,老鐵們包容下啊。
派生類虛表:
1.先將基類的虛表中的內容拷貝一份
2.如果派生類對基類中的虛函數進行重寫,使用派生類的虛函數替換相同偏移量位置的基類虛函數
3.如果派生類中新增加自己的虛函數,按照其在派生類中的聲明次序,放在上述虛函數之后
https://coolshell.cn/articles/12176.html
多態缺陷
●降低了程序運行效率(多態需要去找虛表的地址)
●空間浪費
以上是“C++之多態的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。