基类必须声明虚析构函数,否则通过基类指针delete派生类对象时仅调用基类析构函数,导致派生类资源未释放而泄漏;应声明为virtual ~Base() = default;,即使无资源清理也必须如此。

为什么基类必须声明虚析构函数
当用 new 创建派生类对象,再通过基类指针删除时,若基类析构函数不是 virtual,只会调用基类析构函数,派生类中新增的资源(如堆内存、文件句柄)不会被释放,造成内存泄漏或资源未关闭。这是多态销毁中最隐蔽也最危险的问题。
- 典型错误现象:
delete ptr;后派生类的析构逻辑完全没执行 - 触发条件:基类指针指向派生类对象 +
delete操作 - 不写
virtual的基类析构函数,编译器按静态类型调用,不走虚表
如何正确声明和定义虚析构函数
虚析构函数只需在基类中声明为 virtual,通常定义为空实现(除非需要清理基类资源),派生类析构函数自动成为虚函数,无需显式加 virtual 或 override(但加 override 更安全)。
class Base {
public:
virtual ~Base() = default; // 推荐:= default;或 {} 也可
};
class Derived : public Base {
public:
~Derived() override { / 清理 Derived 特有资源 / }
};
-
= default是最简洁安全的选择,生成编译器默认行为 - 不要写成纯虚析构函数(
virtual ~Base() = 0;),否则必须提供定义 - 即使基类没有资源要清理,也必须声明为
virtual,否则多态删除失效
虚析构函数对性能和 ABI 的影响
添加 virtual 会让类对象隐含一个虚表指针(vptr),增大对象体积(通常 8 字节),并引入一次间接跳转开销。但这仅发生在有虚函数的类上——虚析构本身不会额外增加开销,它只是“占个虚函数槽位”。
- 若类本就含其他虚函数(如
virtual void foo()),加虚析构几乎零成本 - 若类原本无虚函数,仅为了安全加虚析构,则对象大小+8字节,构造/析构略慢(可忽略)
- ABI 层面:一旦加了虚函数,类二进制布局改变,不能跨 ABI 兼容(比如 DLL 接口升级需谨慎)
常见误用和检查建议
容易忽略的是:抽象基类、接口类、甚至只有纯虚函数的类,也必须有虚析构函数。Clang/GCC 的 -Wnon-virtual-dtor 警告能帮你在编译期发现这类问题。
本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
class Interface { public: ~Interface(); };—— 缺virtual - 正确写法:
class Interface { public: virtual ~Interface() = default; }; - CI 中建议开启:
-Wall -Wnon-virtual-dtor(GCC/Clang) - 注意:模板基类、CRTP 基类等特殊场景,虚析构仍适用,别因“是模板”就跳过
虚析构函数不是“可选的最佳实践”,而是多态对象生命周期管理的强制契约。漏掉它,程序可能长期运行无异常,直到某次派生类新增了 new 出来的成员,才突然崩溃或缓慢泄漏。










