我理解的C++虛函數(shù)實現(xiàn)機制

虛函數(shù)使用方法很簡單局装,直接在函數(shù)名前面添加關(guān)鍵字virtual聲明即可碳想,如果虛函數(shù)末尾增加=0則表示為純虛函數(shù)宋渔,純虛函數(shù)要求所有派生類都必須重寫該該函數(shù),帶有純虛函數(shù)的類我們也稱為虛基類督暂。
虛函數(shù)的實現(xiàn)揪垄,作為一個老生常談的問題,要想透徹的講明白逻翁,還是需要對底層機制有進一步的理解的福侈。

問題拋出

基類指針為什么能調(diào)用子類的虛函數(shù)?
虛函數(shù)實現(xiàn)的關(guān)鍵原理和虛函數(shù)表指針vptr有莫大關(guān)系卢未,vptr實際上是指向一個虛函數(shù)表(一維數(shù)組)肪凛,該表存儲了每個虛函數(shù)的函數(shù)地址,那么虛函數(shù)是如何借助這個vptr實現(xiàn)運行時對象的多態(tài)性辽社,也就是我們常說的動態(tài)綁定伟墙。

從C++對象內(nèi)存結(jié)構(gòu)說起

閱讀過《深度探索C++對象模型》的同學應(yīng)該比較熟悉下面這個結(jié)構(gòu),每一個派生類對象實際上是由兩個部分組成滴铅,如下面的圖所示

  • 父類的部分戳葵,包括成員變量、vptr汉匙、成員函數(shù)等拱烁,都是共享給子類(當然有一定的權(quán)限設(shè)置)
  • 子類自身的部分生蚁,子類自己成員變量,成員函數(shù)

所以子類就是個特殊的父類戏自,享有父類所有屬性邦投,是is-a的關(guān)系。所以子類也就能直接強制轉(zhuǎn)換為父類擅笔,我們通常使用dynamic_cast將子類指針轉(zhuǎn)為父類指針志衣,那么這個父類指針的訪問域也就變?yōu)閮?nèi)存模型中的上半部分,無法再訪問子類的任何資源猛们,但是有個例外念脯,那就是虛表指針vptr,為什么呢弯淘?


image-mem.png

我們來進一步看看vptr在類繼承過程中到底是怎么變化绿店?
通常函數(shù)地址都是在編譯的時候就確定了,但是虛函數(shù)的調(diào)用地址需要到運行的時候才能確定庐橙,因為你無法確定一個基類的指針到底是執(zhí)行基類對象還是子類對象假勿。
實際上,虛函數(shù)表指針是在對象執(zhí)行構(gòu)造函數(shù)的確定的怕午。對于基類來說废登,執(zhí)行基類構(gòu)造函數(shù)時淹魄,直接把虛函數(shù)表填充為基類的虛函數(shù)地址即可郁惜;對于派生類來說,派生類對象構(gòu)造的時候甲锡,會先執(zhí)行父類的構(gòu)造函數(shù)(把虛函數(shù)表全部填充為基類的虛函數(shù)地址)兆蕉,然后再執(zhí)行子類構(gòu)造函數(shù)(對于子類重寫的虛函數(shù),修改虛函數(shù)表中對于的函數(shù)地址缤沦,將其改為子類的虛函數(shù)地址)虎韵,具體過程如下圖2個步驟所示:


image-gouzao.png

動態(tài)綁定

有了以上基礎(chǔ)后,回到之前的問題缸废,動態(tài)綁定是怎么發(fā)生的包蓝?
現(xiàn)在回答這個問題很簡單了,對于一個指向子類對象的基類指針企量,它的vptr其實在子類構(gòu)造過程被改寫過测萎,所以使用基指針調(diào)用虛函數(shù)的時候,如果子類有重寫届巩,會調(diào)用子類的虛函數(shù)硅瞧,如果沒有重寫,則直接調(diào)用基類的虛函數(shù)恕汇,這樣就實現(xiàn)了運行時對象的多態(tài)性腕唧。

代碼實例

#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
    virtual void sleep()
    {
        cout<<"animal sleep"<<endl;
    }
    virtual void breathe()
    {
        cout<<"animal breathe"<<endl;
    }
    string name;
};
class Fish:public Animal
{
public:
    virtual void breathe()
    {
        cout<<"fish bubble"<<endl;
    }
    int skin;
};
int main()
{
    Fish fh;
    Fish *pFish = &fh;
    Animal* pAnimal = dynamic_cast<Animal*>(pFish);
    pAnimal->breathe();//fish bubble 執(zhí)行子類重寫的虛函數(shù)
    pAnimal->sleep();  //animal sleep 執(zhí)行基類的虛函數(shù)
    pAnimal->name;     //name位于基類域,能訪問
//    pAnimal->skin; skin位于子類域,無法訪問
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末或辖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子枣接,更是在濱河造成了極大的恐慌颂暇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件月腋,死亡現(xiàn)場離奇詭異蟀架,居然都是意外死亡,警方通過查閱死者的電腦和手機榆骚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門片拍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妓肢,你說我怎么就攤上這事捌省。” “怎么了碉钠?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵纲缓,是天一觀的道長。 經(jīng)常有香客問我喊废,道長祝高,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任污筷,我火速辦了婚禮工闺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓣蛀。我一直安慰自己陆蟆,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布惋增。 她就那樣靜靜地躺著叠殷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诈皿。 梳的紋絲不亂的頭發(fā)上林束,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音稽亏,去河邊找鬼壶冒。 笑死,一個胖子當著我的面吹牛措左,可吹牛的內(nèi)容都是我干的依痊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼胸嘁!你這毒婦竟也來了瓶摆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤性宏,失蹤者是張志新(化名)和其女友劉穎群井,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毫胜,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡书斜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了酵使。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荐吉。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖口渔,靈堂內(nèi)的尸體忽然破棺而出样屠,到底是詐尸還是另有隱情,我是刑警寧澤缺脉,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布痪欲,位于F島的核電站,受9級特大地震影響攻礼,放射性物質(zhì)發(fā)生泄漏业踢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一礁扮、第九天 我趴在偏房一處隱蔽的房頂上張望知举。 院中可真熱鬧,春花似錦深员、人聲如沸负蠕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绣的,卻和暖如春叠赐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屡江。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工芭概, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惩嘉。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓罢洲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惹苗,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一個博客殿较,這個博客記錄了他讀這本書的筆記,總結(jié)得不錯桩蓉×芨伲《深度探索C++對象模型》筆記匯總 1. C++對象模型與內(nèi)...
    Mr希靈閱讀 5,566評論 0 13
  • C++虛函數(shù) C++虛函數(shù)是多態(tài)性實現(xiàn)的重要方式,當某個虛函數(shù)通過指針或者引用調(diào)用時院究,編譯器產(chǎn)生的代碼直到運行時才...
    小白將閱讀 1,727評論 4 19
  • 1. 結(jié)構(gòu)體和共同體的區(qū)別业汰。 定義: 結(jié)構(gòu)體struct:把不同類型的數(shù)據(jù)組合成一個整體伙窃,自定義類型。 共同體un...
    代碼人生ll閱讀 502評論 0 1
  • 1. 虛函數(shù)的定義 允許派生類重新定義與基類同名的函數(shù)样漆,并且可以通過基類指針或引用來訪問基類或派生類的同名函數(shù) 1...
    涼拌姨媽好吃閱讀 1,407評論 0 1
  • 轉(zhuǎn)載:iOS開發(fā)——keychain的使用 通常情況下对供,我們用NSUserDefaults存儲數(shù)據(jù)信息,但是對于一...
    John_LS閱讀 1,426評論 0 1