析构函数是对象生命周期结束时由编译器自动调用的清理钩子,不可手动调用;它必须为public、无参数、不可重载,且不应抛异常,执行顺序为派生类→成员→基类。

析构函数不是“手动调用”的释放工具,而是对象生命周期结束时自动触发的清理钩子;滥用或误解它,反而会导致 double-free、use-after-free 或资源泄漏。
析构函数什么时候被调用
它只在对象**确定要销毁**时由编译器自动调用:栈对象离开作用域、堆对象被 delete、临时对象生命周期结束、容器析构其元素等。你不能也不该写 obj.~MyClass()(除非 placement new 场景,极少见)。
- 常见错误现象:
double free or corruption (fasttop),往往是因为手动调用了析构函数又执行了delete - 使用场景:管理动态内存(
new/delete)、文件句柄(fclose)、锁(pthread_mutex_unlock)、GPU 显存(cudaFree)等非 RAII 封装资源 - 注意:如果类里有
std::vector、std::string等标准容器成员,它们的析构已由标准库保证,你不用重复释放
析构函数必须是 public 且无参数
C++ 标准强制要求析构函数签名是 ~ClassName(),不可带参数、不可重载、不可为 const 或 volatile 限定。访问权限必须是 public(否则 delete 表达式无法调用)。
- 常见错误现象:
error: calling a private destructor of class 'X',多因误将析构函数声明为private或protected - 如果希望禁止外部销毁(如单例或池化对象),应禁用
delete操作符,而不是把析构设为 private —— 正确做法是将operator delete设为private或delete - 性能影响:空析构函数(
~T() = default;)通常被编译器优化掉;但一旦定义了非平凡析构(哪怕只有一行delete p_;),该类型就不再是 trivially destructible,会影响std::vector的移动/销毁策略
析构函数里不能抛异常
如果析构函数抛出未捕获异常,而此时程序已处于栈展开(stack unwinding)过程中(比如另一个异常正在传播),会直接调用 std::terminate 终止程序 —— 这几乎无法调试。
立即学习“C++免费学习笔记(深入)”;
- 常见错误现象:
terminate called without an active exception或程序静默崩溃,根源常是析构中调用了可能抛异常的std::fstream::close()、网络shutdown()等 - 正确做法:在析构函数内用
try/catch(...)吞掉所有异常,或把可能失败的操作移到显式的close()/release()成员函数中,由用户主动调用 - 兼容性影响:C++11 起,析构函数默认带有
noexcept(等价于~T() noexcept);若你显式写了可能抛异常的逻辑,必须加noexcept(false),但这只是“告诉编译器别拦我”,并不能解决根本风险
最易被忽略的一点:析构函数执行时,对象的基类部分和成员变量仍存在,但顺序是「派生类 → 成员 → 基类」;这意味着你不能在析构函数里调用虚函数(虚表已被部分销毁),也不能依赖其他已析构成员的状态 —— 它们可能已经失效。










