1. 程式人生 > >C++執行時通過基類指標或引用呼叫派生類虛擬函式的實現原理: 虛擬函式表

C++執行時通過基類指標或引用呼叫派生類虛擬函式的實現原理: 虛擬函式表

我們知道要實現執行時的多型, 必須在基類中宣告和定義相關的虛擬函式, 並在派生類中重新實現基類中的虛擬函式.
當編譯器見到這種繼承層次結構的時候, 編譯器將為定義了虛擬函式的基類和覆蓋了基類虛擬函式的派生類分別建立一張虛擬函式表(Virtual Function Table, VFT), 也就是說通過編譯器的編譯, 基類和派生類的程式碼中都將有自己的虛擬函式表.
為這些類建立例項化物件時, 會在例項化的物件中插入一個指向對應的虛擬函式表的指標 (VFT*), 該指標通常在存放在物件的開頭區域.
而虛擬函式表(VFT)可以看做就是一個其資料型別為函式指標的靜態陣列, 每一個函式指標指向對應類中的一個虛擬函式.

例如:
基類:

class Base
{
public:
    virtual void Func1()
    {
    // Func1 implementation
    }
    virtual void Func2()
    {
    // Func2 implementation
    }// .. so on and so forth
    virtual void FuncN()
    {
    // FuncN implementation
    }
};

派生類:

class Derived: public Base
{
public:
    virtual
void Func1() { // Func2 overrides Base::Func2() } // no implementation for Func2() virtual void FuncN() { // FuncN implementation } };

類Base和Derived的虛擬函式表

Derived objDerived;
objDerived.Func2();

上述程式碼中編譯器將查詢objDerived物件所屬的Derived類的VFT, 確保能夠呼叫Base::Func2()函式.

void DoSomething(Base& objBase)
{ objBase.Func1(); // invoke Derived::Func1 } int main() { Derived objDerived; DoSomething(objDerived); };

在上述程式碼中, 雖然將派生類的物件objDerived通過引用傳遞給了objBase, 進而被解讀為一個Base類的例項, 但是該例項的VFT*依然指向Derived類的虛擬函式表, 因此通過基類引用呼叫Func1()函式時, 實際上被呼叫的是派生類的虛擬函式Derived::Func1(). 注意必須是基類的指標或引用來呼叫基類虛擬函式, 而不能是直接傳值, 因為傳值會造成物件的切除, 切除派生類物件相對於基類物件多出來的部分, 造成最終呼叫的函式是基類的函式, 而不是我們想要的派生類中的覆蓋版本.

參考文獻: Sams.Teach.Yourself.Cplusplus.in.One.Hour.a.Day.8th.Edition.