Skip to content

在 C++ 中,在析构函数里调用虚函数并不是语法错误,但这样做有严重的隐患,主要体现在多态不会按预期工作,甚至可能导致未定义行为或程序崩溃


🔍 核心原理

1. 对象"身份"退化(vptr 被重置)

C++ 对象析构的顺序是:先调用派生类析构函数,再调用基类析构函数

在进入基类析构函数时:

  • 派生类的成员已经析构完毕
  • 虚函数表指针(vptr)被重设回基类的虚表

👉 因此,在基类析构函数中调用虚函数,只会调用基类自身的版本,而不会动态分派到派生类的覆盖版本,多态彻底失效。1 2 3


2. 访问已析构成员 → 未定义行为

如果派生类重写的虚函数中访问了派生类的成员变量,而此时这些变量已经被析构,就会产生:

  • 访问悬空指针 / 已释放内存
  • 读取无效值
  • 程序崩溃(UB,Undefined Behavior)2 3

3. 调用纯虚函数 → 直接崩溃 💥

如果虚函数在基类中是纯虚函数,在基类析构函数中调用它将导致:

pure virtual method called
terminate called without an active exception

程序立即终止,因为没有有效的函数实现可供调用。1


📌 示例代码

cpp
#include <iostream>

class Base {
public:
    virtual ~Base() {
        func(); // ⚠️ 在析构函数中调用虚函数
    }
    virtual void func() {
        std::cout << "Base::func" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() override {
        // Derived 先被析构
    }
    void func() override {
        std::cout << "Derived::func" << std::endl;
    }
};

int main() {
    Base* p = new Derived;
    delete p;
    // 输出: Base::func  (而不是 Derived::func!)
    // 多态失效,派生类的 func 根本没被调用
}

执行 delete p 时:

  1. 先调用 ~Derived(),Derived 部分析构完毕
  2. 再调用 ~Base(),此时 vptr 已指向 Base 的虚表
  3. func() 实际调用的是 Base::func,多态失效 4 3

✅ 最佳实践与替代方案

问题 替代方案
想在析构时做多态清理 delete 之前手动调用虚函数,再析构
想在析构时通知子类 使用"二段析构"模式,先显式调用 cleanup()
需要工厂模式管理资源 将资源释放逻辑设计成独立的 release() 虚函数,在析构前调用

📖 《Effective C++》条款 9:"绝不在构造和析构过程中调用虚函数"(Never call virtual functions during construction or destruction)——这是 C++ 社区的经典建议。1 4 2 3 5


总结

隐患 说明
多态失效 虚函数只调用当前类版本,不动态分派
未定义行为 派生类成员已析构,访问会导致 UB
程序崩溃 若调用纯虚函数,直接 terminate

一句话总结:析构函数中调用虚函数,多态不生效,轻则逻辑错误,重则程序崩溃,务必避免!


  1. 不要在构造函数或析构函数中调用虚函数 - belief73 - 博客园
  2. 构造函数和析构函数中能否调用虚函数? - 知乎
  3. C++ 为何构造函数和析构函数中不能调用虚函数? | Moushuai
  4. C++ 在构造函数和析构函数中调用虚函数究竟会怎么样?- CSDN博客
  5. C++中虚函数与构造/析构函数的深度解析-腾讯云开发者社区

基于 VitePress 构建