虚析构函数是基类含虚函数时的强制安全要求,否则通过基类指针delete派生对象将跳过派生类析构逻辑,导致资源泄漏或未定义行为;必须声明为virtual,推荐使用virtual ~Base() = default;。

多态性在 C++ 中依赖虚函数机制实现,而虚析构函数不是“可选优化”,是基类含虚函数时的强制安全要求——否则通过基类指针 delete 派生对象会触发未定义行为,且通常不报错、只静默泄漏或崩溃。
为什么必须用 virtual 声明基类析构函数
当用 Base* 指向 Derived 对象并调用 delete 时,若 Base::~Base() 非虚,编译器只调用 Base 的析构函数,跳过 Derived 中的清理逻辑(如释放堆内存、关闭文件句柄等)。
典型现象:
- 程序运行无报错,但 valgrind 显示内存泄漏
- 派生类中
std::ofstream未正确关闭,导致文件内容丢失 - 析构函数里加了
std::cout 却只打印一次(基类的),派生类那行根本没执行
virtual 析构函数的写法与隐式规则
只要基类有至少一个虚函数(如 virtual void func() = 0;),其析构函数就应声明为虚。写法很简单:
立即学习“C++免费学习笔记(深入)”;
class Base {
public:
virtual ~Base() = default; // 推荐:= default 更清晰,且保持 noexcept
// 或 explicit 定义:
// virtual ~Base() {}
};
注意点:
- 不必在派生类中显式写
virtual—— 析构函数自动继承虚属性 - 若基类析构函数是
= default,它默认是noexcept;手动定义时若抛异常,可能终止程序(C++11 起析构函数默认noexcept(true)) - 纯虚析构函数必须提供定义(哪怕空实现),否则链接失败:
virtual ~Base() = 0;→ 还需在 .cpp 中写Base::~Base() {}
哪些情况可以不写虚析构函数
仅当满足全部条件时才可省略:
- 该类明确设计为“不作为基类使用”(比如
std::string、std::vector) - 类中没有虚函数(包括虚析构本身)
- 你确保永远不会出现
delete指向派生对象的基类指针(例如完全不用裸指针管理多态对象,只用std::unique_ptr且其类型擦除已隐含虚析构调用)
但实践中,只要类有 virtual 成员函数,就默认加上 virtual ~ClassName() —— 成本极低,风险极高。
多态性实现的最小必要条件
C++ 运行时多态 ≠ 仅仅有虚函数,而是三者缺一不可:
- 基类中至少一个函数声明为
virtual(含析构) - 通过基类指针或引用调用该函数(不能是值传递或直接对象调用)
- 派生类中重写(
override)该函数,且签名完全匹配(返回类型协变除外)
常见误操作:
- 忘记在派生类函数后加
override,导致因参数类型不一致(如intvsconst int&)而意外变成新函数而非重写 - 用
Base b = Derived{};值传递,发生对象切片,后续调用永远走基类实现 - 把虚函数设为
private,虽语法合法,但外部无法多态调用,失去意义
虚析构函数真正关键的时刻,往往发生在你没写它、也没立刻出错,直到某个深夜调试内存泄漏时才意识到——它不声不响,但决定对象能否被完整回收。










