析构函数必须是public、无参、无返回值,且不能为const或重载;基类析构应声明为virtual以确保派生类资源正确释放;其核心职责是清理动态资源,禁止抛异常、调用虚函数或重复释放RAII对象。

析构函数必须是 public 且无参无返回值
如果你在类里写了一个带参数的 ~ClassName(),编译器会直接报错:「destructor cannot have parameters」。析构函数由编译器自动调用,时机固定(对象生命周期结束时),所以不能手动传参,也不能重载。它只能是 ~ClassName() 这种签名,且通常应声明为 public —— 如果设为 private 或 protected,会导致栈上对象无法销毁(编译错误)或堆上对象无法 delete(链接错误)。
- 不要写
~MyClass(int x)、virtual ~MyClass() const(const无效) - 基类析构函数建议加
virtual,否则通过基类指针delete派生类对象时,派生类部分不会被析构 - 如果类没写析构函数,编译器会生成默认版本(空实现),但只要涉及动态内存、文件句柄、锁等资源,就必须自己定义
什么时候必须手动释放资源
析构函数的核心职责是「清理」,不是「逻辑处理」。常见需手动干预的资源包括:new 出来的内存、fopen 打开的文件、pthread_mutex_init 创建的锁、Windows 的 CreateThread 句柄等。C++ 不回收这些,不写析构函数就会泄漏。
- 用
new[]分配数组,析构里必须用delete[],不能只写delete - 若类中持有
std::unique_ptr或std::vector,它们自身会自动清理,你不用再管——这是 RAII 的好处,别画蛇添足 - 避免在析构函数里抛异常(C++11 起默认是
noexcept),否则程序可能直接终止;真要处理错误,用日志或断言
析构函数里不能调用虚函数
对象析构时,虚函数表指针会逐步回退到基类状态,此时调用虚函数实际执行的是当前类(正在析构的那个类)的版本,而不是最终派生类的重写版。这和构造过程对称,但容易误以为还能多态。
class Base {
public:
virtual ~Base() { foo(); } // 调用的是 Base::foo()
virtual void foo() { cout << "Base"; }
};
class Derived : public Base {
public:
void foo() override { cout << "Derived"; }
};
创建 Derived d; 后析构,输出是 Base,不是 Derived。所以别依赖析构中虚函数的多态行为。
立即学习“C++免费学习笔记(深入)”;
成员变量的析构顺序是确定的
类内声明顺序决定析构顺序:**后声明的先析构**。这和构造顺序相反,但严格固定。如果两个成员有依赖(比如 A 持有 B 的引用,B 必须晚于 A 析构),就得靠声明顺序来保证。
- 基类子对象总是在派生类成员之后析构(即:派生类成员 → 基类成员)
- 局部对象按定义逆序析构,全局/静态对象按定义逆序、跨编译单元顺序未定义——这点容易引发「静态析构顺序灾难」
- 别在析构函数里访问已被析构的成员(比如在 A 的析构里调用 B 的方法,而 B 在 A 之前声明)
new,能用容器就别手管内存,析构体里只做明确必要的清理,别的交给 RAII。


















