C++虛擬函式表(多型的實現原理)

2020-07-16 10:04:21
“多型”的關鍵在於通過基礎類別指標或參照呼叫一個虛擬函式時,編譯時不確定到底呼叫的是基礎類別還是派生類的函數,執行時才確定。這是如何實現的呢?

請看下面的程式,該程式演示了多型類物件儲存空間的大小。
#include <iostream>
using namespace std;
class A
{
public:
    int i;
    virtual void func() {}
    virtual void func2() {}
};
class B : public A
{
    int j;
    void func() {}
};
int main()
{
    cout << sizeof(A) << ", " << sizeof(B);  //輸出 8,12
    return 0;
}
在 32 位編譯模式下,程式的執行結果是:
8, 12

如果將程式中的 virtual 關鍵字去掉,輸出結果變為:
4, 8

對比發現,有了虛擬函式以後,物件所佔用的儲存空間比沒有虛擬函式時多了 4 個位元組。實際上,任何有虛擬函式的類及其派生類的物件都包含這多出來的 4 個位元組,這 4 個位元組就是實現多型的關鍵——它位於物件儲存空間的最前端,其中存放的是虛擬函式表的地址。

每一個有虛擬函式的類(或有虛擬函式的類的派生類)都有一個虛擬函式表,該類的任何物件中都放著該虛擬函式表的指標(可以認為這是由編譯器自動新增到建構函式中的指令完成的)。

虛擬函式表是編譯器生成的,程式執行時被載入記憶體。一個類的虛擬函式表中列出了該類的全部虛擬函式地址。例如,在上面的程式中,類 A 物件的儲存空間以及虛擬函式表(假定類 A 還有其他虛擬函式)如圖 1 所示。


圖1:類A物件的儲存空間以及虛擬函式表