在構造函數和析構函數中不要調用 virtual 函數,因為這類調用永遠不會下降到 derived class(相對於當前正在執行構造/析構的那一層)。
class Transaction {
public:
Transaction() { logTransaction(); } // 呼叫 virtual
virtual void logTransaction() const = 0;
};
class BuyTransaction : public Transaction {
virtual void logTransaction() const { cout << "BuyTransaction\n"; }
};
// main: BuyTransaction b; // 鏈接錯誤:呼叫了純虛函數
原因:構造基類時,對象類型尚為基類,虛函數表指向基類版本。此時調用純虛函數導致鏈接錯誤(或運行期未定義行為)。
class Transaction {
public:
explicit Transaction(const string& logInfo) { logTransaction(logInfo); }
void logTransaction(const string& logInfo) const { cout << logInfo << '\n'; } // non-virtual
};
class BuyTransaction : public Transaction {
public:
BuyTransaction() : Transaction("BuyTransaction") {}
};
derived class 在初始化列表中將必要資訊傳給基類構造函數,基類調用 non-virtual 版本完成日誌記錄。
析構時 derived 部分已銷毀,對象類型退回基類,virtual 調用同樣不會下降。析構函數中應避免 virtual 呼叫。