只有当类作为基类被多态删除时才必须声明虚析构函数,否则不加更轻量安全;典型问题场景是Base* p = new Derived(); delete p;时若Base析构非虚,则Derived析构不执行,导致资源泄漏与未定义行为。

只有当类作为基类被多态使用(比如用 Base* 指向 Derived 对象并 delete 它)时,才必须把析构函数声明为 virtual;否则不加反而更轻量、更安全。
什么时候删基类指针会出问题
典型场景:你写了 Base* p = new Derived(); delete p;,但 Base::~Base() 不是虚函数 —— 此时 C++ 只调用 Base 的析构函数,Derived::~Derived() 完全不会执行。
后果直接可见:
- 派生类里
new出的内存没delete,泄漏 - 打开的文件句柄、持有的锁、注册的回调未清理,程序行为异常
- 对象成员(比如
std::vector、自定义资源管理类)的析构也被跳过,引发连锁问题
虚析构函数不是“让析构变多态”,而是“避免析构被截断”
虚函数表(vtable)在对象构造完成时才就位,而析构过程是反向的:先执行派生类析构体,再自动调用基类析构。虚析构的作用,是在 delete 通过基类指针触发时,让运行时能顺着 vtable 找到真正的动态类型,从而启动完整的析构链。
立即学习“C++免费学习笔记(深入)”;
注意几个关键点:
-
virtual ~Base()声明后,Derived的析构函数自动成为虚函数,无需再写virtual - 即使
Base没有其他虚函数,只要它会被多态删除,就必须加virtual—— 这是独立需求 - 纯虚析构函数(
virtual ~Base() = 0;)必须提供定义体(哪怕空实现),否则链接失败
不加虚析构的常见误判场景
很多人看到“基类”就下意识加 virtual ~Base(),其实没必要,还带来开销:
- 每个对象增加一个虚指针(通常是 8 字节),影响缓存局部性
- 类不能被
trivially_copyable,影响 memcpy、序列化等底层操作 - 如果类根本不会被继承(比如
std::string、std::vector),加虚析构纯属干扰设计意图
判断依据很简单:看有没有 Base* 或 Base& 接收派生类对象并参与 delete 或智能指针管理。没有,就不需要。
最易被忽略的一点:虚析构只解决“通过基类指针删除”这一条路径;如果你用 std::unique_ptr<base> 管理派生对象,同样依赖虚析构 —— 否则 unique_ptr 析构时仍只会调用 Base::~Base()。


















