纯虚析构函数必须提供实现,因为派生类析构时强制调用基类析构函数,链接器需找到其符号;普通纯虚函数则不可实现。

纯虚函数本身就不需要(也不允许)在基类中提供实现——这是它的定义特性;但如果你指的是 virtual ~Base() = 0; 这种纯虚析构函数,那它**必须有实现**,否则链接失败。这是最常被混淆、也最容易导致构建中断的点。
为什么纯虚析构函数必须写函数体?
因为析构调用链是强制的:哪怕基类析构是纯虚的,派生类的析构函数在生成代码时,一定会插入对 Base::~Base() 的调用。链接器会去找这个符号,找不到就报 undefined reference to `Base::~Base()`。
- 普通纯虚函数(如
virtual void foo() = 0;)可以且应该不写实现 - 纯虚析构函数(
virtual ~Base() = 0;)语法上允许声明为纯虚,但语义上要求必须定义 - 实现可以是空的:
Base::~Base() {}或Base::~Base() = default; - 不能只在头文件里声明,还必须在某个源文件里定义(或内联定义)
没实现纯虚析构函数的典型错误现象
编译能过,但链接阶段失败,错误信息非常明确:
undefined reference to `Base::~Base()`
常见触发场景:
- 把纯虚析构函数声明放在头文件,却忘了在 .cpp 文件里写定义
- 用了
inline但没加inline关键字,导致 ODR 违反 - 在模板类里误用纯虚析构(模板类一般不用纯虚析构,因为无法统一定义)
- 跨模块(如 DLL / SO)导出抽象基类时,定义没正确导出或链接
虚析构函数 vs 纯虚析构函数:选哪个?
二者都能解决多态删除的安全问题,但意图不同:
-
virtual ~Base() = default;:类可实例化,只是带虚析构;适合“可实例化的基类” -
virtual ~Base() = 0;+ 定义:强制抽象,类不可实例化;适合纯接口类(如Logger、Renderer) - 如果类已有其他纯虚函数,加纯虚析构是自然选择;否则,虚析构更轻量
- 性能无差异:虚表条目和调用开销完全一样
最容易被忽略的一点:即使你只打算用智能指针(如 std::unique_ptr<base>),只要涉及多态销毁,纯虚析构函数就必须有定义——RAII 不绕过链接规则。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











