C++ 虛函數(shù)表和虛函數(shù)指針機(jī)制

  1. 虛函數(shù)的作用是實(shí)現(xiàn)派生類繼承基類的接口和一個(gè)默認(rèn)的實(shí)現(xiàn)
  2. 那么是如何實(shí)現(xiàn)這種機(jī)制的呢?
    答案:通過(guò)虛函數(shù)表和虛函數(shù)指針實(shí)現(xiàn)。
class Base_VirtualFunction {
public:
    int val;
    virtual void print() {
        cout << "base" << endl;
    }
};

class Base_VirtualFunction1 {
public:
    int val;
   //這里少了virtual
     void print() {
        cout << "base" << endl;
    }
};

    Base_VirtualFunction b;
   //輸出8
    cout << sizeof(b) << endl;

    Base_VirtualFunction1 b1;
   //輸出4
    cout << sizeof(b1) << endl;

為什么加上了一個(gè)virtual之后,對(duì)象的大小就增加了4,就是因?yàn)?,如果一個(gè)類里面存在虛函數(shù),那么這個(gè)類的對(duì)象里面就要保存一個(gè)虛函數(shù)的指針。虛函數(shù)表保存在這個(gè)類里面。

簡(jiǎn)單的做一個(gè)小結(jié):
1 對(duì)于一個(gè)類(這個(gè)類必須是有virtual 函數(shù)的類)只有一張?zhí)摵瘮?shù)表
2 這個(gè)類每創(chuàng)建一個(gè)對(duì)象就有一個(gè)虛函數(shù)指針

tips:
從c++的設(shè)計(jì)者的角度來(lái)說(shuō),如果一個(gè)類沒(méi)有必要設(shè)計(jì)成virtual base class,那么就不要加上virtual。因?yàn)榧尤雟irtual之后,編譯器對(duì)這個(gè)類做的事情,會(huì)遠(yuǎn)遠(yuǎn)比不加入virtual多得多。也就是所,加入virtual之后,效率會(huì)明顯的下降。


具體實(shí)現(xiàn)機(jī)制 to-do

舉一個(gè)例子《深度搜索c++對(duì)象模型》第四章 4.2 虛擬成員函數(shù)
c++對(duì)于虛函數(shù)的多態(tài)行為的解決方案就是:虛函數(shù)表+虛指針

凡是聲明有虛函數(shù)的類,其對(duì)象都含有一個(gè)隱藏的data member,用來(lái)指向該class 的vtbl。這個(gè)隱藏的data member就是vptr(virtual table Pointer)。

假設(shè)我么你有一個(gè)程序,其中有幾個(gè)類型為C1和C2的對(duì)象。類對(duì)象,vptrs,vtbls之間的關(guān)系,如下圖所示:


More effective C++ 條款24 的截圖.png

虛函數(shù)表里面的內(nèi)容是:


image.png

如果C2繼承了C1,那么C2的虛函數(shù)表是:


image.png

那現(xiàn)在考慮一個(gè)這樣的代碼:

void makeCall(C1 * pc1){
     pc1->f1();
}

pc1通過(guò)pc1調(diào)用虛函數(shù)f1,如果只看這段代碼,無(wú)法知道要執(zhí)行哪一個(gè)f1函數(shù)(C1::f1 或者是 C2::f1)會(huì)被調(diào)用,因?yàn)閜c1可能指向一個(gè)c1對(duì)象,也可能指向一個(gè)c2對(duì)象。盡管如此,編譯器仍然需要為pc1調(diào)用f1產(chǎn)生可執(zhí)行的代碼。
所有編譯器必須要產(chǎn)生代碼,完成如下的動(dòng)作:

  1. 根據(jù)對(duì)象的vptr找到vtbl。
C1 * pc1=new C1();
C1->f1();

上述的代碼,根據(jù)指針pc1來(lái)訪問(wèn)pc1指向的對(duì)象(C1 類型的對(duì)象),然后訪問(wèn)這個(gè)對(duì)象里面的虛函數(shù)指針vptr,通過(guò)vptr找到vtbl。這個(gè)vtbl就是C1類的vtbl。

C2 * pc2=new C2();
pc2->f1();

上述的代碼,根據(jù)指針pc1來(lái)訪問(wèn)pc1指向的對(duì)象(C1 類型的對(duì)象),然后訪問(wèn)這個(gè)對(duì)象里面的虛函數(shù)指針vptr,通過(guò)vptr找到vtbl。這個(gè)vtbl就是C2類的vtbl。

  1. 找到被調(diào)用的函數(shù)(f1)在vtbl內(nèi)對(duì)應(yīng)的指針。
  2. 執(zhí)行所指向的函數(shù)。

所以,語(yǔ)句

   pc1->f1();

被編譯器處理之后的代碼是:

//調(diào)用pc1->vptr 所指的vtbl中的第i個(gè)條目所指的函數(shù),pc1被傳給該函數(shù)作為“this”指針?biāo)?(* (pc1->vptr[i]) )(pc1)
參考資料:

這一部分是來(lái)自《more effective c++》的條款24。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容