析构函数在对象生命周期结束时自动调用,包括局部对象离开作用域、动态对象被delete、临时对象表达式结束、容器销毁元素;未配对delete将导致析构函数不执行,引发资源泄漏。

析构函数什么时候被调用
析构函数在对象生命周期结束时自动执行,不是你手动调用的。关键触发时机包括:局部对象离开作用域、动态分配对象被 delete、临时对象结束其表达式生命周期、容器销毁元素(如 std::vector 被析构)。
注意:如果对象是通过 new 分配但没配对 delete,析构函数永远不会运行 —— 这就是内存泄漏+资源泄漏的根源。
为什么不能只靠 delete 就释放资源
delete 的行为分两步:先调用析构函数,再归还内存。但如果你在析构函数里没写清理逻辑,delete 也救不了你。
常见误区是以为“只要 delete p; 就万事大吉”。实际上:
立即学习“C++免费学习笔记(深入)”;
- 文件句柄(
FILE*)不会自动fclose() - 动态分配的内存(
int* buf = new int[100];)不会自动delete[] buf; - Windows 的
HANDLE、Linux 的int fd不会自动关闭 - 第三方库资源(如 OpenGL 的
GLuint texture_id)不会自动glDeleteTextures()
这些都得在析构函数里显式处理。
析构函数里该写什么、不该写什么
该写:释放堆内存、关闭文件/套接字、解锁互斥量(需确保不抛异常)、调用对应的 cleanup API(如 SDL_QuitSubSystem)。
不该写:throw 异常(C++11 起默认为 noexcept,抛异常会直接调用 std::terminate)、调用虚函数(可能已开始析构基类)、访问已析构的成员子对象。
示例:一个管理文件的简单类
class FileGuard {
FILE* fp_;
public:
FileGuard(const char* name) : fp_(fopen(name, "r")) {}
~FileGuard() {
if (fp_) fclose(fp_); // 必须检查空指针
}
// 禁用拷贝,避免双 fclose
FileGuard(const FileGuard&) = delete;
FileGuard& operator=(const FileGuard&) = delete;
};RAII 是析构函数的核心使用场景
RAII(Resource Acquisition Is Initialization)不是语法特性,而是一种模式:把资源获取放在构造函数,释放放在析构函数。这样只要对象存在,资源就有效;对象一销毁,资源必然释放 —— 即使中途抛异常也不会漏。
标准库大量使用它:std::unique_ptr 析构时自动 delete,std::fstream 析构时自动 close(),std::lock_guard 析构时自动 unlock()。
自己实现 RAII 类时最容易忽略的点:必须显式禁用拷贝(或实现移动语义),否则两个对象指向同一资源,析构两次就会崩溃(double-free 或 double-close)。










