virtual是C++面向對象機制核心關鍵字。成員函數加virtual成為虛函數,派生類可重寫實現覆蓋。基類指針指向派生對象時,調用虛函數觸發動態綁定,實現運行時多態。
class Base {
public:
virtual void print() { cout << "Base"; }
};
class Derived : public Base {
public:
void print() { cout << "Derived"; } // 隱式virtual
};
int main() {
Base *p = new Derived();
p->print(); // 輸出 "Derived" — 動態綁定
}
| 維度 | 覆蓋 | 重載 |
|---|---|---|
| 位置 | 基類與派生類 | 同一類內 |
| 函數簽名 | 名、參、返回全同 | 名同,參不同(返回可同可異) |
| virtual | 基類必須virtual | 無關 |
| 綁定 | 動態(運行期) | 靜態(編譯期) |
| 目的 | 多態行為差異化 | 同名語義相關操作便利 |
為什麼重載不看返回類型? 調用處可忽略返回值,編譯器無法從調用語境推導調用哪個函數。
《高質量C++/C編程指南》提出「隱藏規則」:
作者觀點錯誤。實際機制是名字查找規則 + 靜態/動態綁定:
Derived *pd = &d; pd->g(3.14f):pd靜態類型Derived*,名字查找在Derived作用域找到g(int),調用Derived::g(int)(隱式轉換float→int)Base *pb = &d; pb->g(3.14f):pb靜態類型Base*,名字查找在Base作用域找到g(float),無virtual → 靜態綁定調用Base::g(float)核心原則:「只有通過基類指針/引用間接指向派生對象時,多態才生效」(C++ Primer)。Derived*調用永無多態,Base*調用有virtual才多態。無所謂「隱藏規則」,只有名字查找與綁定機制。
class Query {
public:
virtual ostream& print(ostream& = cout) const = 0; // 純虛函數
};
=0標記純虛函數:只提供接口,無實現,不可實例化new Query 編譯錯誤Query *pq = new NameQuery("Nostromo");
class ios { ... }; // 抽象基類
class istream : public ios { ... };
class ostream : public ios { ... };
class iostream : public istream, public ostream { ... }; // 含兩個ios子對象!
弊端:
ios → 浪費空間ios構造兩次ios成員產生二義性(需顯式限定istream::ios::member)ios狀態可能不一致
class Bear : public virtual ZooAnimal { ... }; // virtual位置可前可後
class Raccoon : virtual public ZooAnimal { ... };
virtual是OO落地的關鍵樞紐virtual三重奏:虛函數實現運行時多態,純虛函數定義契約接口,虛繼承解決菱形共享問題。深入理解虛函數表(vptr/vtable)、動態綁定時機、名字查找規則,是C++進階的必經之路。