大家都知道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;
}

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

II、一般繼承(無虛函數(shù)繼承)
假如有如下繼承關(guān)系:

注意到,這個(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é)果中可以得到如下結(jié)論:
在無虛函數(shù)重寫的情況下所實(shí)現(xiàn)的虛函數(shù)表如下:

III、一般繼承(有虛函數(shù)復(fù)寫)
假設(shè)繼承關(guān)系如下:

我們用下面的測試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é)果如下:

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

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)系如下:

我們用下面的測試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é)果如下:

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

V、多重繼承(有虛函數(shù)重寫)
假設(shè)繼承關(guān)系如下:

我們用下面的測試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é)果如下:

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