C++ 建構對象時,基類構造函數先於派生類執行。此時派生類成員尚未初始化。若基類構造函數調用虛函數,虛函數機制會解析至「當前對象類型」——即基類,而非派生類。若派生類覆蓋了該虛函數並訪問自身成員,將導致未定義行為(讀取未初始化內存)。
在基類構造函數執行期間,對象的動態類型就是基類,而非派生類。虛函數表指針 (vptr) 尚未被派生類構造函數更新。同理,析構函數執行順序相反:派生類析構函數先跑,成員變量銷毀後進入基類析構函數,此時對象類型已退化為基類,虛函數調用不會下降至派生類。
class Base {
public:
Base() { init(); } // 危險!
virtual void init() { cout << "Base::init\n"; }
};
class Derived : public Base {
int value;
public:
Derived() : value(42) {}
void init() override { cout << "Derived::init, value=" << value << "\n"; }
};
int main() {
Derived d; // 輸出 "Base::init",value 未被讀取(未初始化時若讀取則 UB)
}
initialize() 非虛函數,構造完成後由客戶端顯式調用。在構造和析構期間不要調用虛函數,因為這類調用從不下調至派生類(相較於當前執行構造/析構函數的那一層)。