多态及其内部原理剖析
多態是面向對象編程三大特性之一,通過多態我們可以大大增強代碼的復用性,也會使代碼整體更緊湊簡潔;
多態可以分為靜態多態和動態多態兩類,
靜態多態就比如函數重載或者運算符重載;
動態多態則是派生類和虛函數實現的運行時的多態;(地址軟綁定
它們就是因為在函數地址綁定時機不同所以有所區分
動態:運行時確定函數地址;
靜態:編譯時確定函數地址;
實現多態需要滿足兩個條件:
1、有繼承關系
2、子類重寫父類中的虛函數(重寫:函數返回值類型 函數名 參數列表 完全一致稱為重寫)
有了這些,我們就可以使用多態了,使用多態同樣需要滿足一個條件:
??父類指針或引用指向子類對象
舉個例子:
先定義一個父類對象,
virtual聲明的函數會變成一個虛函數,這個就是父類中的虛函數,接下來我們聲明兩個子類,
class Son1 : public Father { public:void eyes() {cout<< "單眼皮" << endl;} }; class Son2 : public Father { public:void eyes() {cout<< "單雙眼皮" << endl;} };這里我們聲明了兩個子類對象,都以公共繼承的方式繼承了父類的對象,并且都重寫了父類中的eyes()函數,這時我們寫一個測試函數并在主函數中調用,
void text(Father &v) {v.eyes(); } int main () {Son1 s1;text(s1);// Father &v = s1Son2 s2;text(s2);// Father &v = s2return 0; }輸出結果會是:
單眼皮
單雙眼皮
在調用過程中父類的引用指向了子類的對象,因為父類的eyes是虛函數且子類重寫了該函數,所以該引用就會走子類的eyes,這就是多態的實現;
那么多態的底層原理又是什么呢?
還是剛才的父類,如果我們不加virtual,并計算一下這個父類對象到底占多少空間,我們重寫一下代碼
如果我們在主函數調用text01,輸出為:sizeof Father = 1,即此時的Father只占用一個字節的空間(成員函數eyes不屬于類的對象,它們分開存儲,所以相當于一個空類占用了1一個字節),
那么加上virtual后呢,當我們加上virtual后再運行就會發現輸出變成了:
sizeof Father = 4,這就說明了加上virtual后該類的內容就發生了改變,什么東西占四個字節?整形?單精度浮點型?都不大可能,當然還有一個東西占四個字節,那就是指針,指針始終占四個字節;這里就是一個指針,但是這又是什么類型的指針呢?
??這個指針叫做 :vfptr
v:virtual
f:function
ptr: pointer
直譯過來就是:虛函數(表)指針
回到代碼,此時的Father類的內部就會生成一個vfptr,它會指向一個虛函數表,即??vftable,
那么vftable又是什么?
vftable內部會記錄虛函數的地址,在這個Fathe類中,虛函數表中記錄的地址就是:
&Father :: eyes() 意思就是Fathe作用域下的函數eyes()的地址;
??那么子類Son1繼承后又是怎樣的呢?
如果Son1只單純的繼承Father,并不重寫eyes函數,那么它就會把父類中的所有東西繼承過來,包括父類中的vfptr和vftable,這時繼承過來的的vftable內部依然是
&Father :: eyes()
但是若子類Son1重寫了eyes函數,這時子類就會把虛函數表中的內容進行一個覆蓋操作,此時的子類中的虛函數表內部會替換為子類的虛函數地址,即在這里子類內部就會發生:
&Father :: eyes() 變成 &Son1 :: eyes() ,即 &Father :: eyes() 就被 &Son1 :: eyes() 給覆蓋了,這時子類Son1的虛函數表中就只有 &Son1 :: eyes() 了(Son1的eyes函數地址);
但是父類Father中的虛函數表并沒有被覆蓋,依然是 &Father :: eyes()
??在發生多態時,當父類的指針或者引用指向子類對象時,例如:
Son1 s1; Father &father = s1;當father調用eyes()函數的時候,因為此時的虛函數指針指向的是一個Son1對象,它就會去Son1的虛函數表中找這個eyes函數,這就是在運行階段發生了動態多態。
🛑總的來說就是
1,我們在父類中寫了一個虛函數,使類的內部發生了結構的改變,多了一個虛函數指針(vfptr),指向了一個虛函數表(vftable),虛函數表內部記錄了該虛函數的入口地址;
2,當子類重寫了這個虛函數后,這個子類中的虛函數表內從父類繼承來的虛函數地址就會被子類的函數地址覆蓋。
3,在最后父類的指針或者引用指向子類對象時,就會走子類的重寫的函數地址,就發生了多態。
ps:這就是多態和其內部的剖析,如果有什么問題歡迎交流!
總結
以上是生活随笔為你收集整理的多态及其内部原理剖析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 动态规划(浅层基础)
- 下一篇: 学生管理系统(C++)