不該在構造函數和析構函數期間調用virtual函數,這一點是C++與jave/C#不同的地方之一。
假設有一個class繼承體系,用來模擬股市交易如買進、賣出的訂單等等。這樣的交易一定要經過審計,所以每當創建一個交易對象,在審計日志中也需要創建一筆適當記錄。
正確的做法是在基類Transaction內將logTransaction函數改為non-virtual,然後要求派生類構造函數傳遞必要信息給基類Transaction的構造函數,這樣那個構造函數便可安全地調用non-virtual logTransaction。正確用法如下:
class Transaction { public: Transaction(); ~Transaction(); explicit Transaction(const string& logInfo); void logTransaction(const string& logInfo) const; //non-virtual函數 private: }; Transaction::Transaction(const string& logInfo) { ... logTransaction(logInfo); //非non-virtual調用 }
class BuyTransaction : public Transaction { public: BuyTransaction(parameters) : Transaction(createlogString(parameters)) //將日志信息傳遞給基類構造函數 { ... } ... ~BuyTransaction(); private: static string createlogString(parameters); };
注意示例BuyTransaction內的private static函數 createlogString的運用。比起在成員初值列內給予基類所需的數據,利用輔助函數創建一個值傳遞給基類的構造函數往往比較方便(也比較可讀)。令此函數為static,也就不可能意外指向“初期未成熟的BuyTransaction對象內尚未初始化的成員變量”。這很重要,正是因為“那些成員變量處於未定義狀態”,所以在基類構造和析構期間調用的virtual函數不可下降至派生類。