必须将基类析构函数声明为virtual,否则通过Base*删除派生类对象时仅调用~Base(),导致派生类资源泄漏;虚析构需显式声明并定义,派生类自动继承虚性但须实现清理逻辑,智能指针亦无法绕过该要求。

必须把基类析构函数声明为 virtual,否则通过 Base* 指针 delete 派生类对象时,只会调用 ~Base(),派生类中 new 出的内存、打开的文件、显存等资源完全不会被释放——这是确定会发生的泄漏,不是概率问题。
为什么非虚析构会导致 delete 时只调基类析构
编译器看到 delete base_ptr;,且 base_ptr 类型是 Base*,而 ~Base() 不是 virtual,就直接静态绑定到 ~Base(),根本不去查虚函数表。哪怕 base_ptr 实际指向的是 Derived 对象,~Derived() 也永远不会执行。
常见错误现象:
- 运行时只输出基类析构日志,派生类析构函数静默不触发
-
Derived中的int* data = new int[100];在析构里没被delete[],变成悬空指针,对应堆内存永久丢失 - Valgrind 报告 “definitely lost” 堆块,源头指向派生类构造函数中的
new
虚析构函数怎么写才真正生效
关键不是“加了 virtual”就行,而是声明和定义都得合规:
- 基类中必须显式写
virtual ~Base() = default;或virtual ~Base() { /* 清理基类资源 */ } - 不能只写
virtual ~Base() = 0;(纯虚)却不提供定义体——链接会失败:undefined reference to 'Base::~Base()' - 派生类析构函数不用加
virtual或override,C++ 自动继承虚性;但必须确保它实际执行了清理逻辑(比如delete[] data;) - 如果基类有虚函数(如
virtual void draw() = 0;),那~Base()几乎一定得是虚的——这是强信号
智能指针也不能绕过虚析构要求
用 std::unique_ptr 或 std::shared_ptr 管理对象,照样会出问题:
-
std::unique_ptr—— 析构时仍依赖ptr = std::make_unique (); Base的析构函数是否为虚 - 若
~Base()非虚,ptr离开作用域时仍只调~Base(),Derived资源照漏不误 -
std::shared_ptr例外:它可接受自定义删除器,能绕过基类析构,但属于高成本补救,不该作为设计常态
容易被忽略的坑:虚析构只解决“调不调得到”,不保证“调得对不对”
即使加了 virtual,泄漏仍可能发生:
- 派生类析构函数里用了
delete而不是delete[]释放数组 → 未定义行为,可能 crash 或静默泄漏 - 基类析构函数中调用了虚函数(如
this->cleanup();)→ 此时对象已开始析构,虚表部分失效,行为未定义 - 多层继承中,中间某一级忘了清理自己的资源,只依赖更上层或更下层 —— 虚析构链存在,但清理逻辑断在中间
真正安全的模式是:只要类参与多态销毁(即可能被基类指针指向并 delete),它的析构函数就必须是虚的,且每一级析构函数都必须明确负责自己那一层分配的资源。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











