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