纯虚析构函数必须定义,否则链接失败;接口类析构函数必须为virtual以确保多态销毁安全;是否纯虚取决于是否禁止实例化;现代C++优先使用virtual ~X() = default;。

纯虚析构函数必须有定义体
纯虚析构函数写成 virtual ~ClassName() = 0; 是合法的,但**绝不能只声明不定义**。链接器会报错 undefined reference to 'ClassName::~ClassName()',因为即使纯虚,基类析构函数仍会在派生类析构时被隐式调用(按继承链向上调用)。
正确写法是:声明为纯虚,同时在类外提供空定义:
class Interface {
public:
virtual ~Interface() = 0;
};
Interface::~Interface() {} // 必须存在,哪怕为空
常见错误是误以为“纯虚=不用实现”,结果编译通过但链接失败。
接口类析构函数必须是 virtual
只要可能通过基类指针删除派生对象,析构函数就必须是 virtual。否则会发生未定义行为——仅调用基类析构,派生类成员不被清理(内存泄漏、资源未释放、析构逻辑跳过)。
立即学习“C++免费学习笔记(深入)”;
典型场景包括:
- 工厂函数返回
std::unique_ptr - 容器存的是
std::vector<:unique_ptr>> - 函数参数是
const Interface&,但内部 new 了派生类并试图 delete 指针
注意:virtual 不可省略,哪怕你暂时没写派生类——接口设计要为多态销毁预留契约。
要不要写成纯虚?看是否允许实例化基类
纯虚析构(= 0)的唯一作用是**强制该类为抽象类**,禁止直接构造对象。它和“是否需要多态析构”是两个独立问题。
选择依据:
- 如果这个类纯粹是接口(如
IDrawable),不应被实例化 → 用纯虚析构 - 如果该类也允许作为具体基类使用(比如带默认实现的策略基类),且你希望用户能直接 new 它 → 用普通虚析构(
virtual ~Base() = default;或自定义实现)
二者析构行为完全一致,区别只在能否实例化。别为了“看起来更接口”而滥用纯虚析构,除非你真想封死构造路径。
现代 C++ 推荐写法:default + virtual
C++11 起,若析构逻辑无需手动干预(无裸指针、无 FILE*、无自定义 close),优先用 virtual ~Interface() = default;,而不是空函数体或纯虚。
理由:
-
= default显式表明“用编译器生成的”,语义清晰 - 编译器生成的析构函数是
noexcept的,而手写空函数体不是(除非显式加noexcept) - 避免纯虚带来的抽象类限制,后续扩展更灵活(比如加个 static 工厂方法后仍可实例化)
只有当你明确需要“禁止构造”时,才回到纯虚析构;其他情况,virtual ~X() = default; 更轻量、更安全、更符合现代习惯。
最容易被忽略的一点:即使写了 = default,也必须确保所有成员(尤其是智能指针、RAII 类型)自身析构是 noexcept 的——否则整个析构链可能意外抛异常,导致程序终止。










