基类析构函数必须声明为虚函数,否则通过基类指针删除派生类对象时,派生类析构函数不会执行,导致资源泄漏、未定义行为及链接错误;仅当类不作基类且不通过基类指针管理生命周期时才可省略。

delete 基类指针时派生类析构函数不执行
这是最直接、最危险的表现:当你用 Base* 指向一个 Derived 对象,然后调用 delete ptr,C++ 只会调用 Base::~Base(),而 Derived::~Derived() 完全被跳过。
常见错误现象:
- 控制台只输出 “Base destructor”,完全看不到派生类的清理日志
- 派生类中用
new分配的内存(如int* data = new int[100])永远不会被delete[],造成内存泄漏 - 文件句柄、socket、GPU buffer 等资源未关闭,程序运行越久越卡顿或崩溃
抽象基类没写虚析构函数,链接时报 undefined reference
如果基类有纯虚函数(比如 virtual void func() = 0;),你又忘了声明 virtual ~Base() = 0;,那它还不是抽象类;但更糟的是——即使你补上 virtual ~Base() = 0;,若没提供定义,链接阶段就会失败。
原因很实在:派生类的析构函数在生成代码时,**一定会隐式调用基类析构函数**。没有函数体,链接器找不到实现,报错类似:
立即学习“C++免费学习笔记(深入)”;
undefined reference to `Base::~Base()'
必须补上定义,哪怕空着:
Base::~Base() = default;
或者在类内直接写:
virtual ~Base() = 0 {}构造函数里调用 virtual 函数是安全的,析构函数里不是
很多人误以为“既然构造时能用虚函数,析构时也该一样”。但恰恰相反:构造时对象类型逐步建立,析构时对象类型逐步销毁。在 Base 的析构函数执行期间,Derived 部分已经析构完毕,此时再调用虚函数,行为是未定义的。
所以别在析构函数里调用虚函数——哪怕你加了 virtual,也容易踩坑。真正需要多态清理逻辑的,应该提前拆出来,由用户显式调用,或者用 RAII 封装。
什么时候可以不写 virtual 析构函数?
只有两种情况真正安全:
- 这个类明确不作为基类使用(即没被继承,也没人拿它做接口)
- 这个类的对象永远不用基类指针管理生命周期(比如只栈上分配、或只用智能指针且类型擦除前已知完整类型)
但现实中,只要类名带 Interface、Handler、Strategy,或头文件里出现 = 0,就默认要加 virtual ~ClassName() = default;。漏掉它,不是“可能出问题”,而是“早晚出问题”,而且问题往往出现在上线后、压力测试时、或者换编译器之后。


















