<p>必须加virtual析构函数:当类用于被继承且通过基类指针管理派生类对象生命周期时,如Base* p = new Derived(); delete p;,否则引发未定义行为,导致派生类析构函数不执行、资源泄漏。</p>

因为不加 virtual,用基类指针 delete 派生类对象时,派生类析构函数根本不会被调用,资源就漏了。
什么时候必须加 virtual?
只要这个类设计上是「用来被继承的」,并且你预期会通过基类指针或引用管理派生类对象的生命周期,就必须加。
- 典型场景:工厂函数返回
Base*、容器存std::unique_ptr<base>、回调接口传Base& - 关键判断点:有没有可能写类似
Base* p = new Derived(); delete p;这样的代码 - 如果类里已有其他虚函数(比如
virtual void draw() = 0;),那析构函数几乎一定得是virtual
virtual 析构函数怎么写才对?
声明为 virtual 就够了,不需要也不应该写成纯虚——除非你明确要让基类抽象化且不提供实现(但即便如此,也得定义它)。
- 正确写法:
virtual ~Base() = default;或virtual ~Base() { /* 清理基类资源 */ } - 错误写法:
~Base() = 0;(没定义体 → 链接错误);或virtual ~Base() = 0;却不写Base::~Base() { } - 注意:即使派生类析构函数没显式写
virtual,它也会自动继承虚性
不加 virtual 的实际后果是什么?
不是“可能出错”,而是 C++ 标准明确定义为未定义行为(UB)。常见表现是:
立即学习“C++免费学习笔记(深入)”;
- 只调用基类析构函数,派生类析构函数完全跳过(
delete p;后Derived::~Derived()一行都不执行) - 堆内存泄漏(比如派生类里
new int[1024],但没delete[]) - 文件句柄、socket、GPU buffer 等资源未关闭,程序跑久后崩溃或卡死
- 调试时发现对象“消失”了,但成员变量的析构逻辑压根没走 —— 因为编译器静态绑定了
Base::~Base()
最常被忽略的一点:虚析构函数带来的 vtable 开销虽小,但如果你写的是一个高频创建/销毁的轻量类(比如 Point、Vec2),又确定它绝不会被继承,那就别加 virtual —— 不是因为“怕慢”,而是避免误导后续维护者以为它支持多态。该设计意图,得从类名、文档和使用方式一起守住。


















