Effective_C++09: 绝不在构造和析构过程中调用virtual函数
Effective C++ 条款09:绝不在构造和析构过程中调用virtual函数
核心思想:在构造和析构期间不要调用virtual函数,因为这类调用不会下降至派生类(比起当前执行构造函数和析构函数的那层)。
⚠️ 1. 问题场景:基类构造中调用虚函数
#include <iostream>
class Transaction {
public:Transaction() {logTransaction(); // 构造函数中调用虚函数}virtual void logTransaction() const {std::cout << "Base transaction logged\n";}
};class BuyTransaction : public Transaction {
public:virtual void logTransaction() const override {std::cout << "Buy transaction logged\n";}
};int main() {BuyTransaction b; // 期望调用BuyTransaction的logTransactionreturn 0;
}
输出:
Base transaction logged
原因:
- 当
BuyTransaction
构造时,首先调用基类Transaction
构造函数 - 此时对象类型被视为
Transaction
而非BuyTransaction
- 虚函数机制不会下降到派生类实现
✅ 2. 解决方案:非虚接口模式
(1) 静态方法传递信息(⭐️推荐)
class Transaction {
public:explicit Transaction(const std::string& logInfo) {logTransaction(logInfo); // 非虚函数}void logTransaction(const std::string& logInfo) const {std::cout << logInfo << std::endl;}
};class BuyTransaction : public Transaction {
public:BuyTransaction() : Transaction(createLogString()) {} // 传递信息private:static std::string createLogString() { // 静态方法安全return "Buy transaction logged";}
};
(2) 初始化列表传递信息
class BuyTransaction : public Transaction {
public:BuyTransaction() : Transaction("Buy transaction logged") {} // 直接传递
};
🔍 3. 关键原则
场景 | 操作 | 原因 |
---|---|---|
构造/析构中需定制行为 | 通过参数传递信息,调用非虚函数 | 虚函数机制在构造/析构期间不完整 |
派生类需提供初始化信息 | 使用静态方法生成信息(可访问类静态成员) | 避免使用未初始化的成员变量 |
基类构造函数需要类型相关信息 | 在派生类构造函数初始化列表中传递 | 确保信息在基类构造前可用 |
避免在析构函数中调用虚函数 | 析构顺序与构造相反,同样虚函数机制不完整 | 防止调用已被销毁的派生类成员 |
⚠️ 4. 错误案例:派生类析构中的虚函数
class Base {
public:virtual ~Base() {cleanup(); // 基类析构中调用虚函数}virtual void cleanup() {std::cout << "Base cleanup\n";}
};class Derived : public Base {
public:~Derived() {// 隐式调用Base::~Base()}virtual void cleanup() override {std::cout << "Derived cleanup\n";}
};int main() {Derived d; // 析构时调用Base::cleanup()而非Derived::cleanup()return 0;
}
输出:
Base cleanup
结论:
- 析构顺序:先派生类析构 → 再基类析构
- 基类析构执行时,对象已被视为基类类型
💎 总结:构造析构虚函数三原则
-
绝对避免
构造和析构期间绝不直接调用虚函数。 -
信息传递
通过参数将定制信息传递给基类的非虚函数:class Base { public:Base(const std::string& info) { nonVirtualInit(info); // 非虚函数} };class Derived : public Base { public:Derived() : Base(generateInfo()) {} private:static std::string generateInfo() { return "Custom data"; } };
-
静态方法优先
在派生类中使用静态方法生成信息,避免访问未初始化的成员变量。