摘要:當(dāng)子類繼承了父類并且子類重寫了父類的虛函數(shù)之后,我們可以看到此時子類中虛函數(shù)指針對應(yīng)的虛函數(shù)表中存的是子類經(jīng)過重寫的函數(shù)了。
前言:相信小伙伴們在學(xué)習(xí)到C++面向?qū)ο筇匦灾坏亩鄳B(tài)的時候,都或多或少有一些疑惑。搞不清楚多態(tài)在底層是如何實(shí)現(xiàn)的,今天我就帶大家刨析一下多態(tài)的底層實(shí)現(xiàn),了解一下虛函數(shù)指針和虛函數(shù)表到底是什么東西?(注意本文操作環(huán)境是VS2019 x86架構(gòu) 32位機(jī)器)
1.1.1 定義:
多態(tài)按字面的意思就是多種形態(tài)。當(dāng)類之間存在層次結(jié)構(gòu),并且類之間是通過繼承關(guān)聯(lián)時,就會用到多態(tài)。
C++ 多態(tài)意味著調(diào)用成員函數(shù)時,會根據(jù)調(diào)用函數(shù)的對象的類型來執(zhí)行不同的函數(shù)。
----------來自菜鳥教程
1.2.1 多態(tài)分為兩類:
·靜態(tài)多態(tài):函數(shù)重載和運(yùn)算符重載屬于靜態(tài)重載,復(fù)用函數(shù)名
·動態(tài)多態(tài):派生類和虛函數(shù)實(shí)現(xiàn)運(yùn)行時多態(tài)
1.2.2 靜態(tài)多態(tài)和動態(tài)多態(tài)的區(qū)別:
·靜態(tài)多態(tài)的函數(shù)地址早綁定–編譯階段確定函數(shù)地址
·動態(tài)多態(tài)的函數(shù)地址晚綁定–運(yùn)行階段確定函數(shù)地址
2.1 靜態(tài)多態(tài)代碼:
#include using namespace std;class Father{public: void speak() { cout << "爸爸在說話!" << endl; }};class Son :public Father{public: void speak() { cout << "兒子在說話!" << endl; }};//執(zhí)行說話函數(shù)//地址早被綁定 在編譯階段確定函數(shù)地址void doSpeak(Father &father)//父類引用接收子類對象{ father.speak();}void test01(){ Son son; doSpeak(son);}int main(){ test01(); return 0;}
2.2 運(yùn)行結(jié)果:
2.3 分析:
在此案例中,派生類和基類中都出現(xiàn)了speak函數(shù),當(dāng)用父類指針或者引用接收子類對象時,程序會執(zhí)行基類中的同名函數(shù),這是為什么呢?因?yàn)楦割愔械膕peak函數(shù)地址在編譯期間就被綁定,所以在執(zhí)行程序時無論傳遞的是哪種對象,執(zhí)行的都是基類中的speak函數(shù)。這就是靜態(tài)動態(tài)的弊端,那么如果想實(shí)現(xiàn)傳入哪種對象就執(zhí)行哪種類的函數(shù),這就需要用到動態(tài)多態(tài)了。
動態(tài)多態(tài)滿足條件:
1、有繼承關(guān)系
2、子類重寫父類的虛函數(shù)
動態(tài)多態(tài)的使用:
父類的指針或者引用指向子類對象
3.1.1動態(tài)多態(tài)代碼
#include using namespace std;class Father{public: virtual void speak() { cout << "爸爸在說話!" << endl; }};class Son :public Father{public: void speak() { cout << "兒子在說話!" << endl; }};//執(zhí)行說話函數(shù)//地址早被綁定 在編譯階段確定函數(shù)地址void doSpeak(Father &father)//父類引用接收子類對象{ father.speak();}void test01(){ Son son; doSpeak(son);}int main(){ test01(); return 0;}
3.1.2 運(yùn)行結(jié)果:
3.1.3 分析:
靜態(tài)多態(tài)變?yōu)閯討B(tài)多態(tài)只需要給父類的函數(shù)加上virtual關(guān)鍵字變?yōu)樘摵瘮?shù)。
小知識:在C++中空類也占內(nèi)存,占一個字節(jié)的空間
3.2.1
我們先來看一下父類的函數(shù)前加上virtual關(guān)鍵字,父類的內(nèi)存占用有什么變化?
通過運(yùn)行發(fā)現(xiàn)此時父類所占的空間變成了4個字節(jié),那么這四個字節(jié)到底是存了什么????其實(shí)聰明的小伙伴們可能已經(jīng)猜出來這四個字節(jié)是什么了,沒錯存的就是一個指針。這個指針就叫虛函數(shù)指針,簡寫為vfptr(virtual function pointer). 對于一個類來說,如果類中存在虛函數(shù),那么編譯器就會自動在類中加一條生成虛函數(shù)指針的語句(void * vfptr),并且在類的構(gòu)造函數(shù)中為虛函數(shù)指針進(jìn)行賦值(vfptr=&Father::vtal),此時這個虛函數(shù)指針就會指向虛函數(shù)表。所以,如果對象存在虛函數(shù),那么編譯器就會生成一個指向虛函數(shù)表的指針,所有的虛函數(shù)都存在于這個表中,虛函數(shù)表就可以理解為一個數(shù)組,每個單元用來存放虛函數(shù)的地址。
3.2.2
此時我們在父類中寫兩個虛函數(shù)
然后打開vs2019的調(diào)試功能查看一下對象father可以看到
此時可以發(fā)現(xiàn)父類對象確實(shí)有一個vfptr指針,這個指針對應(yīng)的
表里就是儲存著兩個虛函數(shù)的地址,這兩個函數(shù)都是屬于父類的。
3.2.3
當(dāng)子類繼承了父類并且子類重寫了父類的虛函數(shù)之后,我們可以看到:
此時子類中虛函數(shù)指針對應(yīng)的虛函數(shù)表中存的是子類經(jīng)過重寫的函數(shù)了。所以當(dāng)傳入一個子類對象時通過查詢子類vfptr找到對應(yīng)的虛函數(shù)表,從而找到其中存的函數(shù)地址去執(zhí)行,這也就是為什么動態(tài)多態(tài)可以根據(jù)傳入對象的不同來執(zhí)行不同的語句。
3.2.4 畫圖演示
由此證明,我們的結(jié)論是正確的!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/119812.html
摘要:也就是說,一個實(shí)例變量,在的對象初始化過程中,最多可以被初始化次。當(dāng)所有必要的類都已經(jīng)裝載結(jié)束,開始執(zhí)行方法體,并用創(chuàng)建對象。對子類成員數(shù)據(jù)按照它們聲明的順序初始化,執(zhí)行子類構(gòu)造函數(shù)的其余部分。 類的拷貝和構(gòu)造 C++是默認(rèn)具有拷貝語義的,對于沒有拷貝運(yùn)算符和拷貝構(gòu)造函數(shù)的類,可以直接進(jìn)行二進(jìn)制拷貝,但是Java并不天生支持深拷貝,它的拷貝只是拷貝在堆上的地址,不同的變量引用的是堆上的...
摘要:繼承方式繼承方式限定了基類成員在派生類中的訪問權(quán)限,包括公有的私有的和受保護(hù)的。所以子類給父類引用賦值也是可以的,相當(dāng)于給子類對象中繼承的父類部分起了別名。如圖成員函數(shù)也是如此,當(dāng)子類與父類具有函數(shù)名相同的函數(shù)時,還是符合就近原則。 ...
摘要:博主在公眾號后臺設(shè)置了關(guān)鍵字回復(fù),回復(fù)下面的里面的內(nèi)容,可免費(fèi)獲得學(xué)習(xí)視頻和資料。 博主在公眾號后臺設(shè)置了關(guān)鍵字回復(fù), 回復(fù)下面的【】里面的內(nèi)容, 可免費(fèi)獲得C++學(xué)習(xí)視頻和資料。 如回復(fù):C++基礎(chǔ) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 【C++】 【1】...
閱讀 2430·2021-11-15 11:38
閱讀 3617·2021-09-22 15:16
閱讀 1256·2021-09-10 11:11
閱讀 3248·2021-09-10 10:51
閱讀 3072·2019-08-30 15:56
閱讀 2847·2019-08-30 15:44
閱讀 3242·2019-08-28 18:28
閱讀 3582·2019-08-26 13:36