C++動(dòng)態(tài)綁定的實(shí)現(xiàn)(虛函數(shù)表)

大家都知道C++動(dòng)態(tài)綁定的實(shí)現(xiàn)是通過虛函數(shù)體現(xiàn)的,再深入研究一下,程序是如何知道該調(diào)用哪個(gè)函數(shù)呢?這就涉及到了虛函數(shù)的底層實(shí)現(xiàn)問題,類時(shí)通過虛函數(shù)表來完成虛函數(shù)尋址的。

具體來說就是,當(dāng)我們用父類指針來操作一個(gè)子類時(shí),這張?zhí)摵瘮?shù)表就閃亮登場了,它就像一張地圖一樣,指示這實(shí)際應(yīng)該調(diào)用的函數(shù)。下面我們就來探索一下這張表到底有多神奇。

I、無繼承實(shí)現(xiàn)

C++的標(biāo)準(zhǔn)規(guī)格說明書中說到,編譯器必須要保證虛函數(shù)表的指針存在于對象實(shí)例中最前面的位置(這是為了保證正確的取得要調(diào)用虛函數(shù)的偏移量)。

根據(jù)上面引用的說法,我們有如下測試代碼1:

#include<iostream>

using namespace std;

class Base {
public:
    virtual void f() {cout << "Base::f" << endl;}
    virtual void g() {cout << "Base::g" << endl;}
    virtual void h() {cout << "Base::h" << endl;}
};


int main()
{
    typedef void(*Fun)(void);
    Base b;
    Fun pFun = nullptr;
    cout << "虛函數(shù)表地址:" << (int*)(&b) << endl;
    cout << "虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b) << endl;

    pFun = (Fun)*((int*)*(int*)(&b));    //Base::f
    cout << "第一個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&b) + 1);   //Base::g
    cout << "第二個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;


    pFun = (Fun)*((int*)*(int*)(&b) + 2);   //Base::h
    cout << "第三個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    return 0;
}

測試1輸出結(jié)果

從測試結(jié)果中可以得到下面的虛函數(shù)尋址方式示意圖:

虛函數(shù)表示意圖1

II、一般繼承(無虛函數(shù)繼承)

假如有如下繼承關(guān)系:

繼承關(guān)系示意圖1

注意到,這個(gè)繼承關(guān)系中,子類沒有重寫任何父類的函數(shù)。那么在派生實(shí)例中虛函數(shù)表該是什么樣子的,接下來進(jìn)行如下測試2:

#include<iostream>

using namespace std;

class Base {
public:
    virtual void f() {cout << "Base::f" << endl;}
    virtual void g() {cout << "Base::g" << endl;}
    virtual void h() {cout << "Base::h" << endl;}
};

class Derive : public Base {
public:
    virtual void f1() {cout << "Derive::f1" << endl;}
    virtual void g2() {cout << "Derive::g1" << endl;}
    virtual void h1() {cout << "Derive::h1" << endl;}
};


typedef void(*Fun)(void);

int main()
{
    Base b;
    Derive d;
    Fun pFun = nullptr;
    cout << "父類虛函數(shù)表地址:" << (int*)(&b) << endl;
    cout << "父類虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b) << endl;

    cout << "子類虛函數(shù)表地址:" << (int*)(&d) << endl;
    cout << "子類虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&d) << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+0);
    cout << "第一個(gè)虛函數(shù)為:";      //Base::f
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+1);   //Base::g
    cout << "第二個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;


    pFun = (Fun)*((int*)*(int*)(&d)+2);    //Base::h
    cout << "第三個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+3);    //Derive::f1
    cout << "第四個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+4);    //Derive::g1
    cout << "第五個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+5);   //Derive::h1
    cout << "第六個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    return 0;
}

測試2結(jié)果:

測試2輸出結(jié)果

從測試2輸出結(jié)果中可以得到如下結(jié)論:
在無虛函數(shù)重寫的情況下所實(shí)現(xiàn)的虛函數(shù)表如下:

虛函數(shù)表示意圖2

III、一般繼承(有虛函數(shù)復(fù)寫)

假設(shè)繼承關(guān)系如下:

繼承關(guān)系示意圖2

我們用下面的測試3代碼觀測虛函數(shù)表:

#include<iostream>

using namespace std;

class Base {
public:
    virtual void f() {cout << "Base::f" << endl;}
    virtual void g() {cout << "Base::g" << endl;}
    virtual void h() {cout << "Base::h" << endl;}
};

class Derive : public Base {
public:
    void f() override {cout << "Derive::f" << endl;}
    virtual void g2() {cout << "Derive::g1" << endl;}
    virtual void h1() {cout << "Derive::h1" << endl;}
};


typedef void(*Fun)(void);

int main()
{
    Base b;
    Derive d;
    Fun pFun = nullptr;
    cout << "父類虛函數(shù)表地址:" << (int*)(&b) << endl;
    cout << "父類虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b) << endl;

    cout << "子類虛函數(shù)表地址:" << (int*)(&d) << endl;
    cout << "子類虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&d) << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+0);      //Derive::f
    cout << "第一個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+1);   //Base::g
    cout << "第二個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;


    pFun = (Fun)*((int*)*(int*)(&d)+2);    //Base::h
    cout << "第三個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+3);    //Derive::g1
    cout << "第四個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+4);    //Derive::h1
    cout << "第五個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)(&d)+5);   //無函數(shù),會出現(xiàn)一個(gè)error
    cout << "第六個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    return 0;
}

測試結(jié)果如下:

測試3輸出結(jié)果

由測試結(jié)果可以得出在有虛函數(shù)重寫的子類中虛函數(shù)表如下圖所示:

虛函數(shù)表示意圖3

IV、多重繼承(無虛函數(shù)重寫)

多重繼承會為一個(gè)子類對象產(chǎn)生多個(gè)虛函數(shù)表,這也是能通過我們上面的結(jié)論得出的,因?yàn)楦鶕?jù)測試2,我們發(fā)現(xiàn)父類對象的第一個(gè)虛函數(shù)與子類對象的第一個(gè)虛函數(shù)地址相同,所以如果子類繼承自多個(gè)父類,則分別繼承自多個(gè)父類的虛函數(shù)表應(yīng)該各自有不同的起始地址,再根據(jù)偏移量尋址得到其他虛函數(shù)地址。

假設(shè)繼承關(guān)系如下:

繼承關(guān)系示意圖3

我們用下面的測試4代碼觀測虛函數(shù)表:

#include<iostream>

using namespace std;

class Base1 {
public:
    virtual void f() {cout << "Base1::f" << endl;}
    virtual void g() {cout << "Base1::g" << endl;}
    virtual void h() {cout << "Base1::h" << endl;}
};

class Base2 {
public:
    virtual void f() {cout << "Base2::f" << endl;}
    virtual void g() {cout << "Base2::g" << endl;}
    virtual void h() {cout << "Base2::h" << endl;}
};

class Base3 {
public:
    virtual void f() {cout << "Base3::f" << endl;}
    virtual void g() {cout << "Base3::g" << endl;}
    virtual void h() {cout << "Base3::h" << endl;}
};

class Derive : public Base1, public Base2, public Base3 {
public:
    virtual void f1() {cout << "Derive::f1" << endl;}
    virtual void g1() {cout << "Derive::g1" << endl;}
};


typedef void(*Fun)(void);

int main()
{
    Base1 b1;
    Base2 b2;
    Base3 b3;
    Derive d;
    Fun pFun = nullptr;
    cout << "Base1虛函數(shù)表地址:" << (int*)(&b1) << endl;
    cout << "Base1虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b1) << endl;

    cout << "Base2虛函數(shù)表地址:" << (int*)(&b2) << endl;
    cout << "Base2虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b2) << endl;

    cout << "Base3虛函數(shù)表地址:" << (int*)(&b3) << endl;
    cout << "Base3虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b3) << endl;

    cout << "子類虛函數(shù)表地址:" << (int*)(&d) << endl;
    cout << "子類虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&d) << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); //Base1::f
    cout << "第一個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);   //Base1::g
    cout << "第二個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;


    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);    //Base1::h
    cout << "第三個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);    //Derive::f1
    cout << "第四個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+4);    //Derive::g1
    cout << "第五個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);     //Base2::f
    cout << "第六個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);     //Base2::g
    cout << "第七個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+1)+2);     //Base2::h
    cout << "第八個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+2)+0);     //Base3::f
    cout << "第九個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+2)+1);     //Base3::g
    cout << "第十個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+2)+2);     //Base3::h
    cout << "第十一個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    return 0;
}

測試結(jié)果如下:

測試4輸出結(jié)果

從輸出結(jié)果中我們可以得到如下的虛函數(shù)表示意圖:

虛函數(shù)表示意圖4

V、多重繼承(有虛函數(shù)重寫)

假設(shè)繼承關(guān)系如下:

繼承關(guān)系示意圖4

我們用下面的測試5代碼觀測虛函數(shù)表:

#include<iostream>

using namespace std;

class Base1 {
public:
    virtual void f() {cout << "Base1::f" << endl;}
    virtual void g() {cout << "Base1::g" << endl;}
    virtual void h() {cout << "Base1::h" << endl;}
};

class Base2 {
public:
    virtual void f() {cout << "Base2::f" << endl;}
    virtual void g() {cout << "Base2::g" << endl;}
    virtual void h() {cout << "Base2::h" << endl;}
};

class Base3 {
public:
    virtual void f() {cout << "Base3::f" << endl;}
    virtual void g() {cout << "Base3::g" << endl;}
    virtual void h() {cout << "Base3::h" << endl;}
};

class Derive : public Base1, public Base2, public Base3 {
public:
    void f() override {cout << "Derive::f" << endl;}
    virtual void g1() {cout << "Derive::g1" << endl;}
};


typedef void(*Fun)(void);

int main()
{
    Base1 b1;
    Base2 b2;
    Base3 b3;
    Derive d;
    Fun pFun = nullptr;
    cout << "Base1虛函數(shù)表地址:" << (int*)(&b1) << endl;
    cout << "Base1虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b1) << endl;

    cout << "Base2虛函數(shù)表地址:" << (int*)(&b2) << endl;
    cout << "Base2虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b2) << endl;

    cout << "Base3虛函數(shù)表地址:" << (int*)(&b3) << endl;
    cout << "Base3虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&b3) << endl;

    cout << "子類虛函數(shù)表地址:" << (int*)(&d) << endl;
    cout << "子類虛函數(shù)表--第一個(gè)函數(shù)地址:" << (int*)*(int*)(&d) << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); //Derive::f
    cout << "第一個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);   //Base1::g
    cout << "第二個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;


    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);    //Base1::h
    cout << "第三個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);    //Derive::g1
    cout << "第四個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);    //Derive::f
    cout << "第五個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);     //Base2::g
    cout << "第六個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+1)+2);     //Base2::h
    cout << "第七個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+2)+0);     //Derive::f
    cout << "第八個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+2)+1);     //Base3::g
    cout << "第九個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    pFun = (Fun)*((int*)*(int*)((int*)&d+2)+2);     //Base3::h
    cout << "第十個(gè)虛函數(shù)為:";
    pFun();
    cout << endl;

    return 0;
}

測試結(jié)果如下:

測試5輸出結(jié)果

從輸出結(jié)果中我們可以得到如下的虛函數(shù)表示意圖:

虛函數(shù)表示意圖5

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

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