首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Effective C++ 條款 09:構造與析構期間絕不調用虛函數

     編輯:關於C++

為什麼危險?

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)
}

正確替代方案

  1. 非虛介面模式 (NVI):基類提供非虛公有函數,內部調用私有虛函數,派生類覆蓋私有虛函數。構造/析構時調用公有非虛函數,避開虛函數機制。
  2. 延遲初始化:提供單獨的 initialize() 非虛函數,構造完成後由客戶端顯式調用。
  3. 工廠函數:構造對象後再調用虛函數,封裝於工廠中。

關鍵提醒

在構造和析構期間不要調用虛函數,因為這類調用從不下調至派生類(相較於當前執行構造/析構函數的那一層)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved