不写虚析构函数会导致通过基类指针删除派生类对象时派生类析构函数不被调用,引发内存泄漏、资源未释放等未定义行为;只要类设计为被继承且可能通过基类指针删除,就必须声明virtual ~base() = default。

虚析构函数不写会怎样?
如果你在基类里没把析构函数声明为 virtual,而用基类指针删除派生类对象,~Derived() 根本不会被调用——只调用 ~Base()。内存泄漏、资源未释放、析构逻辑跳过,全会发生。
常见错误现象:
- 对象里有
new出来的内存,没被delete - 文件句柄、socket、mutex 没被正确关闭或解锁
- 调试时发现派生类的析构体根本没进断点
这不是“可能出错”,是 C++ 标准明确定义的未定义行为(UB)。哪怕当前编译器看起来“运行正常”,换平台、换优化等级就崩。
哪些类必须加 virtual ~Base()?
只要这个类设计为被继承,并且你**有可能通过基类指针/引用去 delete 对象**,就必须加。
立即学习“C++免费学习笔记(深入)”;
使用场景包括:
- 多态容器:比如
std::vector<:unique_ptr>></:unique_ptr>存了Derived实例 - 工厂函数返回
std::shared_ptr<base> - 回调系统中传入
Base*,后续由框架负责销毁
反例:纯接口类(如 class ILogger { virtual void log(...) = 0; };)也得加——它几乎肯定会被继承和多态删除。
本文档主要讲述的是用Apache Spark进行大数据处理——第一部分:入门介绍;Apache Spark是一个围绕速度、易用性和复杂分析构建的大数据处理框架。最初在2009年由加州大学伯克利分校的AMPLab开发,并于2010年成为Apache的开源项目之一。 在这个Apache Spark文章系列的第一部分中,我们将了解到什么是Spark,它与典型的MapReduce解决方案的比较以及它如何为大数据处理提供了一套完整的工具。希望本文档会给有需要的朋友带来帮助;感
virtual ~Base() 和 = default 的区别
写了 virtual ~Base() = default; 和空实现 virtual ~Base() {} 效果一样,但前者更明确表达“我不要自定义逻辑,只是需要虚性”。
注意点:
- 如果基类已有用户定义的析构函数(哪怕只有一行
std::cout ),就不能再用 <code>= default,否则编译报错 - 派生类析构函数自动继承虚性,不用再写
virtual,但加了也不错——语义更清晰 - 虚析构函数会轻微增加对象大小(vptr),但这是必须付出的代价;别为了这点开销放弃虚性
示例对比:
class Base {
public:
virtual ~Base() = default; // ✅ 推荐
};
class Derived : public Base {
public:
~Derived() override { /* 自动虚,可加 override 提醒 */ }
};有没有例外情况?
极少。唯一常见例外是:类明确禁止被继承(C++11 起可用 final),且你 100% 确保没人用基类指针删它。
但现实里很难保证——比如同事加了个新子类,忘了改析构函数,问题就埋下了。
所以更稳妥的做法是:
- 所有带虚函数的基类,一律加
virtual ~Class() = default; - 没有虚函数但想支持多态删除?先加个虚函数(比如
virtual void dummy() {}),再加虚析构——不过这通常说明设计有问题
真正容易被忽略的是:模板基类、CRTP 基类、甚至 std::exception 派生类,都得检查析构是否虚。不是只有“大块头”继承体系才需要。








