虛擬成員函式

成員函式也可以宣告為 virtual 在這種情況下,如果呼叫指標或對例項的引用,則不會直接訪問它們; 相反,他們將查詢虛擬函式表中的函式(虛擬函式的指向成員函式的列表,通常稱為 vtablevftable),並使用它來呼叫適合例項動態的版本(實際)型別。如果直接從類的變數呼叫函式,則不執行查詢。

struct Base {
    virtual void func() { std::cout << "In Base." << std::endl; }
};

struct Derived : Base {
    void func() override { std::cout << "In Derived." << std::endl; }
};

void slicer(Base x) { x.func(); }

// ...

Base b;
Derived d;

Base *pb = &b, *pd = &d; // Pointers.
Base &rb = b, &rd = d;   // References.

b.func();   // Output:  In Base.
d.func();   // Output:  In Derived.

pb->func(); // Output:  In Base.
pd->func(); // Output:  In Derived.

rb.func();  // Output:  In Base.
rd.func();  // Output:  In Derived.

slicer(b);  // Output:  In Base.
slicer(d);  // Output:  In Base.

請注意,雖然 pdBase*,而 rdBase&,但是在兩個叫 Derived::func() 而不是 Base::func() 中的任何一個上呼叫 func(); 這是因為 Derivedvtable 更新了 Base::func() 條目,而不是指向 Derived::func()。相反,請注意如何將例項傳遞給 slicer() 總是會導致 Base::func() 被呼叫,即使傳遞的例項是 Derived; 這是因為有一些稱為資料切片的東西,其中通過值將 Derived 例項傳遞到 Base 引數會使得 Derived 例項中不屬於 Base 例項的部分無法訪問。

當成員函式定義為 virtual 時,所有具有相同簽名的派生類成員函式都會覆蓋它,無論覆蓋函式是否指定為 virtual。然而,這可能使得派生類更難以被程式設計師解析,因為沒有關於哪些函式是什麼的指示 23。

struct B {
    virtual void f() {}
};

struct D : B {
    void f() {} // Implicitly virtual, overrides B::f.
                //  You'd have to check B to know that, though.
};

但是請注意,派生函式只有在其簽名匹配時才會覆蓋基函式; 即使派生函式明確宣告為 virtual,如果簽名不匹配,它也會建立一個新的虛擬函式。

struct BadB {
    virtual void f() {}
};

struct BadD : BadB {
    virtual void f(int i) {} // Does NOT override BadB::f.
};

Version >= C++ 11

從 C++ 11 開始,可以使用上下文相關關鍵字 override 明確覆蓋意圖。這告訴編譯器程式設計師希望它覆蓋基類函式,這會導致編譯器在沒有覆蓋任何內容時省略錯誤。

struct CPP11B {
    virtual void f() {}
};

struct CPP11D : CPP11B {
    void f() override {}
    void f(int i) override {} // Error: Doesn't actually override anything.
};

這也有利於告訴程式設計師該函式既是虛擬的,又在至少一個基類中宣告,這可以使複雜的類更容易解析。

當一個函式宣告為 virtual,並在類定義之外定義時,virtual 說明符必須包含在函式宣告中,而不是在定義中重複。

Version >= C++ 11

這也適用於 override

struct VB {
    virtual void f(); // "virtual" goes here.
    void g();
};
/* virtual */ void VB::f() {} // Not here.
virtual void VB::g() {} // Error.

如果基類過載 virtual 函式,則只有明確指定為 virtual 的過載才是虛擬的。

struct BOverload {
    virtual void func() {}
    void func(int) {}
};

struct DOverload : BOverload {
    void func() override {}
    void func(int) {}
};

// ...

BOverload* bo = new DOverload;
bo->func(); // Calls DOverload::func().
bo->func(1); // Calls BOverload::func(int).

有關更多資訊,請參閱相關主題