Effective C++ 条款 09:绝不在构造和析构过程中调用 virtual 函数
文章目录
- 条款 09:绝不在构造和析构过程中调用 virtual 函数
- 关键原则
- 示例问题:构造期间调用 `virtual` 函数
- 设计建议
- 总结
条款 09:绝不在构造和析构过程中调用 virtual 函数
关键原则
-
避免构造或析构期间调用
virtual
函数
在构造函数或析构函数内部调用virtual
函数,只会调用当前类版本的函数,而不会调用 derived class 中的覆写版本。 -
构造和析构期间的对象行为
当构造函数执行时,对象尚未完全构造完成;当析构函数执行时,派生类的成员可能已经被销毁。这导致virtual
函数无法正确工作。
示例问题:构造期间调用 virtual
函数
以下代码演示了在构造期间调用 virtual
函数的问题:
class Base { public: Base() { call(); } // 在构造函数中调用 virtual 函数 virtual void call() const { std::cout << "Base::call" << std::endl; } }; class Derived : public Base { public: Derived() {} virtual void call() const override { std::cout << "Derived::call" << std::endl; } }; int main() { Derived d; // 输出:Base::call return 0; }
解释:
- 在构造
Derived
对象时,Base
的构造函数调用了call
函数。 - 因为
Derived
的部分尚未完成初始化,调用的是Base::call
而不是Derived::call
。
设计建议
-
避免构造或析构期间调用
virtual
函数
构造和析构期间对象处于特殊状态,避免不确定性。 -
延迟行为到完全初始化后执行
使用辅助函数显式触发需要派生类支持的行为,例如:class Base { public: Base() {} void init() { call(); } // 提供显式调用函数 virtual void call() const { std::cout << "Base::call" << std::endl; } }; class Derived : public Base { public: Derived() {} virtual void call() const override { std::cout << "Derived::call" << std::endl; } }; int main() { Derived d; d.init(); // 显式触发行为 return 0; }
-
以 non-virtual 函数完成构造和析构期间的任务
通过 non-virtual 函数实现相关任务,避免对未完全构造或已销毁部分的调用。
总结
- 在构造和析构期间调用
virtual
函数是危险且不可预测的。 - 明确区分构造/析构期间和对象完全构造后的行为。
- 借助显式函数调用或 non-virtual 函数解决该问题。