必须声明为虚析构函数,否则通过base*删除派生类对象时派生类析构函数不执行,导致资源泄漏;仅当类不被继承或不用基类指针管理生命周期时可不加virtual。

不是必须,但只要基类可能被继承、且用户会用 Base* 指针指向派生类对象并调用 delete,那就必须声明为虚函数——否则派生类析构函数根本不会执行。
什么时候不加 virtual 也能安全删除?
当类明确不参与多态:不被继承,或虽被继承但绝不用基类指针管理派生类生命周期。例如:
-
std::string、std::vector等标准容器类,内部有虚函数但析构非虚——因为你不该用std::vector<int>*</int>去delete一个派生类对象(它们也不允许被继承) - 纯接口类(如只含纯虚函数)若不打算用
new/delete管理其对象,也可暂不加virtual,但风险极高,不推荐 - 局部栈对象(
Derived d;)或智能指针(std::unique_ptr<base>)自动调用正确析构,不依赖虚析构——但前提是std::unique_ptr<base>的模板参数已知为完整类型,否则仍可能出问题
为什么 delete Base* 时派生类析构不运行?
因为 C++ 默认按静态类型决定调用哪个析构函数。没有 virtual,编译器在编译期就绑定到 Base::~Base(),跳过运行时类型识别。
常见错误现象:
- 派生类中用
new分配的内存未释放 → 内存泄漏 - 文件句柄、socket、GPU buffer 等资源未关闭 → 程序运行一段时间后崩溃或拒绝服务
- 调试时发现
Derived::~Derived()完全没进断点,但Base::~Base()执行了
示例:
class Base {
public:
~Base() { std::cout <h3>virtual ~Base() 之后要注意什么?</h3><p>加了 <code>virtual</code> 只是起点,不是万能解药:</p>
- 析构函数必须是
public:私有虚析构会导致delete ptr编译失败(即使派生类重写为public也不行) - 避免在析构函数里抛异常:C++ 标准规定,若析构中抛异常且当前正因另一个异常栈展开,则直接调用
std::terminate() - 不要在虚析构里调用虚函数:此时虚表指针已开始销毁,行为未定义;例如
this->cleanup()若是虚函数,结果不可控 - 多重继承下,所有含虚函数的基类都应有虚析构:哪怕只是间接基类,否则对应子对象的清理仍可能跳过
std::unique_ptr 能绕过虚析构吗?
不能完全绕过。如果构造时用的是 std::unique_ptr<base>(new Derived),且 Base::~Base() 非虚,行为仍是未定义的——即使编译通过,运行时仍只调 Base 析构。
但有两个例外场景看似“安全”:
- 用
std::make_unique<derived>()</derived>创建,再转型为std::unique_ptr<base>:此时删除器保存的是Derived的完整类型信息,能正确析构(依赖删除器类型擦除) - 显式指定删除器:
std::unique_ptr<base>(new Derived, [](Base* p) { delete static_cast<derived>(p); })</derived>—— 但这等于手动绕过语言机制,易错且难维护
真正省心的做法,还是老老实实把 Base::~Base() 声明为 virtual。
最常被忽略的一点:虚析构函数一旦加了,就不能再内联(除非整个类定义在头文件且你确认所有翻译单元看到同一份定义),否则可能引发 ODR 违规——链接时出现“undefined reference to vtable for Base”之类错误,尤其在跨动态库使用时。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











