日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

详解虚函数的实现过程之单继承(2)

發(fā)布時(shí)間:2025/3/21 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 详解虚函数的实现过程之单继承(2) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

從匯編分析一下下面的多態(tài)模擬結(jié)構(gòu)



利用 父類指針指向子類的特性,可以間接調(diào)用各子類中的虛函數(shù)。
雖然指針類型為父類,但由于虛表的排列順序是按虛函數(shù)在類繼承層次中首次聲明的順序依次排列的,因此,只要繼承了父類,其派生類的虛表中的父類部分的排列就與父類的一致,子類新定義的虛函數(shù)將會(huì)按照聲明順序緊跟其后。

我們給Speak函數(shù)傳遞任何一個(gè)基于CPerson的派生對(duì)象地址都能正確調(diào)用虛函數(shù)ShowSpeak,那么當(dāng)我們調(diào)用虛函數(shù)時(shí),它是如何實(shí)現(xiàn)調(diào)用的呢?請(qǐng)看下面分析

分析如下:

地址401108的eax是this指針,即對(duì)象的地址,
然后40110B是取了this指針?biāo)傅牡胤降闹?#xff08;也就是虛表指針的值),即虛表的地址傳給edx,
最后edx+4也就是虛表里面第二個(gè)元素的地址,然后[edx+4]即第二個(gè)元素的值(因?yàn)閿?shù)組元素是一個(gè)函數(shù)指針),也就是第二個(gè)虛函數(shù)ShowSpeak的地址。

由于虛函數(shù)采用間接調(diào)用機(jī)制,因此在使用父類指針pPerson調(diào)用虛函數(shù)時(shí),沒有依照其作用域調(diào)用CPerson類中定義的成員函數(shù)ShowSpeak

當(dāng)父類中定義有虛函數(shù)時(shí),將會(huì)產(chǎn)生虛表。當(dāng)父類的派生類產(chǎn)生對(duì)象時(shí),將會(huì)調(diào)用子類的構(gòu)造函數(shù)前優(yōu)先調(diào)用父類構(gòu)造函數(shù),并以子類對(duì)象的首地址作為this指針傳給父類構(gòu)造函數(shù)。

在父類構(gòu)造函數(shù)中,會(huì)先初始化子類虛表指針為父類的虛表首地址。此時(shí),如果在父類構(gòu)造函數(shù)中調(diào)用虛函數(shù),雖然虛表指針屬于子類對(duì)象,但指向的地址卻是父類的虛表首地址,這是可判斷虛表所屬作用域相同,于是轉(zhuǎn)成直接調(diào)用,從而造成構(gòu)造函數(shù)內(nèi)的虛函數(shù)失效



這里也就是在構(gòu)造函數(shù)里面調(diào)用了 虛函數(shù),會(huì)出現(xiàn) 多態(tài)的失效。

父類構(gòu)造函數(shù)會(huì)在子類構(gòu)造函數(shù)之前運(yùn)行,在執(zhí)行父類構(gòu)造函數(shù)時(shí)將虛表指針修改為當(dāng)前類的虛表指針,即父類的虛表指針,導(dǎo)致虛函數(shù)特性失效。

如果父類構(gòu)造函數(shù)內(nèi)部存在虛函數(shù)調(diào)用的話,這樣的順序能防止在子類中構(gòu)造父類時(shí),父類會(huì)根據(jù)虛表錯(cuò)誤地調(diào)用子類的成員函數(shù)。

大家心里會(huì)不會(huì)出現(xiàn)一個(gè)疑問:直接讓編譯器把構(gòu)造函數(shù)或析構(gòu)函數(shù)中的虛函數(shù)調(diào)用修改為直接調(diào)用方式,不就可以避免這類問題了嗎????
但是大家別忘了,程序員仍然可以自己編寫其它成員函數(shù)間接調(diào)用本類中聲明的其它虛函數(shù)。

舉個(gè)例子:
假設(shè)類A中定義了成員函數(shù)f1()和虛函數(shù)f2(),而且類B繼承類A并重寫f2()。根據(jù)前面的講解我們可以知道,在子類B的構(gòu)造函數(shù)執(zhí)行前會(huì)調(diào)用父類A的構(gòu)造函數(shù),此時(shí)如果在類A的構(gòu)造函數(shù)中調(diào)用了f1(),顯然不會(huì)構(gòu)成多態(tài),編譯器會(huì)直接調(diào)用f1()的代碼,但是,如果在f1()中調(diào)用了f2(),此時(shí)就會(huì)產(chǎn)生間接調(diào)用的指令,形成多態(tài)。如果類B的對(duì)象的虛表指針沒有更換為類A的虛表指針,就會(huì)導(dǎo)致在訪問類B 的虛表后調(diào)用到類B中的f2()函數(shù),而此時(shí)類B的對(duì)象尚未完成構(gòu)造,其數(shù)據(jù)成員時(shí)不確定的,這時(shí)在f2()中引用類B的對(duì)象中的數(shù)據(jù)成員是很危險(xiǎn)的。

同理,在析構(gòu)類B的對(duì)象時(shí),會(huì)先執(zhí)行類B的析構(gòu)函數(shù),然后執(zhí)行類A 的析構(gòu)函數(shù)。如果在類A的析構(gòu)函數(shù)中調(diào)用f1(),顯然不能構(gòu)成多態(tài),編譯器同樣會(huì)產(chǎn)生直接調(diào)用f1()的代碼。但是,如果f1()中又調(diào)用了f2(),此時(shí)會(huì)構(gòu)成多態(tài),如果這個(gè)對(duì)象的虛表指針沒有更換為類A 的虛表指針,同樣也會(huì)導(dǎo)致訪問虛表并調(diào)用類B中的f2()。但是,此時(shí)B類對(duì)象已經(jīng)執(zhí)行過析構(gòu)函數(shù)了,所以B類中定義的數(shù)據(jù)已經(jīng)不可靠了,對(duì)其進(jìn)行操作是很危險(xiǎn)的

這也就是我們經(jīng)常說的在構(gòu)造函數(shù)和析構(gòu)函數(shù)里面會(huì)發(fā)生虛表覆蓋

析構(gòu)總結(jié):
首先調(diào)用自身的析構(gòu)函數(shù),然后調(diào)用成員對(duì)象的析構(gòu)函數(shù),最后調(diào)用父類的析構(gòu)函數(shù)。
在對(duì)象析構(gòu)時(shí),首先設(shè)置虛表指針為自身虛表,再調(diào)用自身的析構(gòu)函數(shù)。
如果有成員對(duì)象,則按申明順序以 倒序方式依次調(diào)用成員對(duì)象的析構(gòu)函數(shù)。
最后,調(diào)用父類析構(gòu)函數(shù)。在調(diào)用父類析構(gòu)函數(shù)的時(shí)候,會(huì)設(shè)置虛表指針為父類自身的虛表。

構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用流程如下:



分析如下:
首先調(diào)用了父類的構(gòu)造函數(shù),然后設(shè)置虛表指針為當(dāng)前類的虛表首地址。而析構(gòu)函數(shù)中的順序卻與構(gòu)造函數(shù)相反,首先設(shè)置虛表指針為當(dāng)前類的虛表首地址,然后調(diào)用父類的析構(gòu)函數(shù)。其構(gòu)造和析構(gòu)過程如下:
構(gòu)造:基類------>基類的派生類------->…………------->當(dāng)前類
析構(gòu):當(dāng)前類------>基類的派生類------->…………------->基類
虛函數(shù)系列:
詳解虛函數(shù)的實(shí)現(xiàn)過程之初探虛表(1)
詳解虛函數(shù)的實(shí)現(xiàn)過程之單繼承(2)
詳解虛函數(shù)的實(shí)現(xiàn)過程之多重繼承(3)
詳解虛函數(shù)的實(shí)現(xiàn)過程之虛基類(4)
詳解虛函數(shù)的實(shí)現(xiàn)過程之菱形繼承(5)

總結(jié)

以上是生活随笔為你收集整理的详解虚函数的实现过程之单继承(2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。