不加virtual的析构函数会导致delete基类指针时必然跳过派生类析构函数,引发内存泄漏等资源未释放问题;加virtual后通过虚函数表动态绑定,确保先调用派生类析构再调用基类析构。

不加 virtual 的析构函数,delete 基类指针时根本不会调用派生类析构函数——资源泄漏不是“可能”,而是必然发生。
基类指针 delete 派生类对象时发生了什么
编译器默认按静态类型决定调用哪个析构函数。如果 Base* 指向 Derived 对象,而 Base::~Base() 不是虚函数,delete ptr 就只调用 Base::~Base(),Derived::~Derived() 完全被跳过。
常见错误现象:
- 程序运行无崩溃,但内存持续增长(
Derived中new的内存未delete) - 文件句柄、socket、GPU 显存等未关闭,后续操作失败或报错(如
Too many open files) - 调试时发现
Derived析构函数断点从未命中
virtual ~Base() 怎么改变调用行为
加上 virtual 后,析构调用走 vtable 动态绑定,运行时根据实际对象类型决定从哪一层开始析构。销毁顺序固定为:派生类析构 → 成员变量析构(逆声明序)→ 基类析构。
使用场景明确:
- 类被设计为基类,且预期通过基类指针/引用管理派生类生命周期(如工厂函数返回
std::unique_ptr<base>) - 类中已有其他虚函数(比如
virtual void draw() = 0;),此时不加虚析构是典型疏漏 - STL 容器存储基类指针(如
std::vector<:shared_ptr>></:shared_ptr>),删除时依赖多态析构
纯虚析构函数为什么必须定义实现
声明 virtual ~Base() = 0; 会让类变成抽象类,但链接阶段会报错:undefined reference to 'Base::~Base()'。因为即使纯虚,派生类析构末尾仍会隐式调用 Base::~Base()。
正确写法必须两步:
- 在类内声明:
virtual ~Base() = 0; - 在类外提供空定义:
Base::~Base() {}(哪怕什么都不做,也得存在)
参数差异不存在——析构函数没有参数;性能影响极小,vtable 开销仅在首次构造对象时产生,远小于资源泄漏代价。
什么时候可以不写虚析构
只有当一个类**确定不会被继承**,或**永远不会通过基类指针被删除**时,才可省略。例如:
- 工具类(
StringUtils)、策略类(std::less)等无状态、不用于多态的类 - 基类仅用于模板特化(如
std::iterator),不参与运行时多态 - 所有派生类都用值语义或智能指针管理,且基类析构本身不涉及资源释放
但只要存在一丝多态删除的可能性,就该加 virtual——这个判断比事后查内存泄漏要便宜得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











