纯虚析构函数必须定义,否则链接失败;因为派生类销毁时必调用基类析构函数,链接器需其符号定义,而编译器不自动补空实现是因析构语义关乎资源清理责任。

纯虚析构函数不定义就会链接失败
因为派生类对象销毁时,编译器生成的析构逻辑一定会调用基类析构函数,哪怕它是纯虚的。链接器需要找到 Base::~Base() 的符号定义,否则报错: undefined reference to 'Base::~Base()'。这不是运行时错误,而是链接阶段就卡住——你甚至看不到程序启动。
- 普通纯虚函数可以只声明不定义(比如
virtual void foo() = 0;),但析构函数是例外 - 即使写成内联空实现
virtual ~Base() = 0 {}(C++11起允许),也等价于提供了定义 - 如果忘了定义,且基类有派生类,
delete ptr_to_derived;会直接导致链接失败,不是崩溃,是构建失败
为什么编译器不自动提供空实现
C++标准明确要求用户显式定义,原因在于析构函数语义上承担资源清理责任。编译器不能擅自假设“空实现是安全的”——比如基类中持有 FILE* 或引用计数指针,可能需要在析构里 fclose 或 dec_ref。强制定义就是强制设计者思考:这里是否真该做点什么?
- 空实现
Base::~Base() {}是合法且常见,但它必须是你写的,不是编译器补的 - 若基类有成员变量需析构(如
std::vector<int></int>),其析构会隐式调用,但基类自己的逻辑仍需你把控 - 这个规则和
= default无关:virtual ~Base() = default;是虚析构,不是纯虚析构
纯虚析构函数和普通虚析构函数怎么选
是否纯虚,只影响类能否实例化;是否虚,只影响多态删除是否安全。两者目的不同,但常一起出现。如果你的类本就是抽象基类(已有其他纯虚函数),加纯虚析构只是顺手强化抽象性;如果类本可实例化,就别用纯虚析构,改用普通虚析构。
- 已有
virtual void draw() = 0;→ 加virtual ~Drawable() = 0;并定义,保持抽象性一致 - 类没有其他纯虚函数,但会被继承 → 用
virtual ~Base() = default;或virtual ~Base() {}即可 - 误用
virtual ~Base() = 0;又不定义,是最典型的新人坑,错误信息不提示“你忘了定义”,只甩个链接错误
定义位置和写法细节
定义必须出现在某个翻译单元(.cpp 文件)中,或直接在类定义内用 = 0 {} 写成内联形式。不能只在头文件里声明,也不建议在头文件里放非内联定义(引发 ODR 违规)。
立即学习“C++免费学习笔记(深入)”;
- 推荐写法(头文件中):
virtual ~Base() = 0 {}—— 简洁、内联、无链接风险 - 传统写法(头文件声明 + .cpp 定义):
virtual ~Base() = 0;+Base::~Base() {} - 禁止写法:
virtual ~Base() = 0;仅声明,不跟任何定义
new 出基类对象,只看你有没有派生类,以及是否可能通过基类指针销毁它——只要存在这种可能性,定义就不可省。


















