class Base {
public:
virtual void function(int arg = 1) { cout << arg << endl; }
};
class Derive : public Base {
public:
virtual void function() { cout << "Derive" << endl; }
virtual void function(int arg) { cout << "Derive " << arg << endl; }
};
int main() {
Base* obj = new Derive();
obj->function(); // 輸出 "Derive 1"
}
雖然呼叫的是衍生類別的 function(),但預設參數值 **1 來自基底類別**,因為預設參數是**靜態繫結** (編譯期決定),而虛擬函式是**動態繫結** (執行期決定)。
Base*)Derive*)
Base* pgo = new Derive; // 靜態: Base*, 動態: Derive*
Asterioid* pa = new Asterioid; // 靜態/動態皆為 Asterioid*
pgo = pa; // 靜態仍為 Base*, 動態變為 Asterioid*
Base& rgo = *pa; // 靜態 Base&, 動態 Asterioid
若預設參數採動態繫結,編譯器需在執行期為虛擬函式決定適當預設值,會增加複雜度與效能開銷。C++ 選擇**效率與簡潔**,犧牲此情境的直覺性。
class B { public: void mf(); };
class D : public B { public: void mf(); }; // 重新定義 (隱藏)
B* pB = new D;
D* pD = new D;
pB->mf(); // 呼叫 B::mf (靜態類型 B*)
pD->mf(); // 呼叫 D::mf (靜態類型 D*)
同一物件、同一函式名稱,卻因指標靜態類型不同而有不同行為 —— **「雙面行為」**。
若 mf 為虛擬函式,pB->mf() 與 pD->mf() 皆呼叫 D::mf,行為一致。
mf,產生矛盾:
無論從哪個角度,**必須無條件禁止重新定義繼承而來的非虛擬函式**。
多型基類的解構函式**必須為虛擬**。若基類解構函式非虛擬,刪除基類指標指向的衍生物件時,衍生類解構函式不會被呼叫。這本質上是條款 36 的特例:編譯器自動產生的解構函式屬於非虛擬函式,衍生類會隱含重新定義。
virtual