> 技术文档 > Effective_C++09: 绝不在构造和析构过程中调用virtual函数

Effective_C++09: 绝不在构造和析构过程中调用virtual函数


Effective C++ 条款09:绝不在构造和析构过程中调用virtual函数


核心思想在构造和析构期间不要调用virtual函数,因为这类调用不会下降至派生类(比起当前执行构造函数和析构函数的那层)。

⚠️ 1. 问题场景:基类构造中调用虚函数
#include 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的logTransaction return 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

结论

  • 析构顺序:先派生类析构 → 再基类析构
  • 基类析构执行时,对象已被视为基类类型

💎 总结:构造析构虚函数三原则

  1. 绝对避免
    构造和析构期间绝不直接调用虚函数。

  2. 信息传递
    通过参数将定制信息传递给基类的非虚函数:

    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\"; }};
  3. 静态方法优先
    在派生类中使用静态方法生成信息,避免访问未初始化的成员变量。