C++析构函数:对象销毁时的清理工作

1. 什么是析构函数?

想象你用C++创建了一个对象,就像买了一个杯子。当杯子用完后,你需要清洗它并放回原处,对吧?在C++中,对象销毁时也需要做“清理”工作,这就是析构函数的作用。

简单来说:
- 构造函数是创建对象时自动调用的函数,负责初始化对象。
- 析构函数是销毁对象时自动调用的函数,负责清理对象使用的资源(比如动态分配的内存、打开的文件等)。

2. 析构函数的定义格式

析构函数的语法很特殊,需要记住以下几点:
- 名字必须与类名相同,但开头多一个波浪线 ~(发音“tilde”)。
- 没有参数,也没有返回值类型(连 void 都不能写)。
- 一个类只能有一个析构函数(不能重载)。

示例:

class MyClass {
public:
    // 构造函数(创建对象时调用)
    MyClass() { 
        cout << "对象创建了!" << endl; 
    }
    // 析构函数(销毁对象时调用)
    ~MyClass() { 
        cout << "对象销毁了,资源已清理!" << endl; 
    }
};

3. 析构函数的核心作用:清理资源

为什么需要析构函数?最常见的原因是避免资源泄漏。比如:
- 如果对象用 new 动态分配了内存(int* p = new int;),如果不主动释放,内存会一直被占用,导致“内存泄漏”。
- 如果对象打开了文件(如 fstream file("test.txt");),如果不关闭文件,可能导致文件资源浪费或数据丢失。

示例:动态内存清理

class Array {
private:
    int* data; // 动态数组指针
    int size; // 数组大小
public:
    Array(int s) : size(s) {
        data = new int[size]; // 构造时分配内存
        cout << "构造函数:分配了" << size << "个int的内存" << endl;
    }
    ~Array() {
        delete[] data; // 析构时释放内存,避免泄漏!
        cout << "析构函数:释放了数组内存" << endl;
    }
};

int main() {
    Array arr(5); // 创建对象,调用构造函数
    // arr的作用域仅在main函数内,离开main时自动销毁,调用析构函数
    return 0;
}

输出结果

构造函数:分配了5个int的内存
析构函数:释放了数组内存

4. 析构函数何时被调用?

C++会在以下场景自动调用析构函数,无需手动触发:

  • 对象离开作用域:比如函数内的局部对象,函数结束时会调用析构函数。
  void test() {
      MyClass obj; // 函数内定义的局部对象
      // 函数结束时,obj被销毁,调用析构函数
  }
  int main() {
      test(); // 函数test执行完毕,obj的析构函数被调用
      return 0;
  }
  • delete 销毁动态对象:如果对象是用 new 创建的(即指针指向的对象),用 delete 时会调用析构函数。
  int main() {
      MyClass* p = new MyClass(); // 动态创建对象
      delete p; // 销毁对象,调用析构函数
      return 0;
  }
  • 临时对象的销毁:函数参数或返回值中的临时对象,在表达式结束时会被销毁。
  MyClass createObj() {
      return MyClass(); // 创建临时对象并返回,函数结束时临时对象销毁
  }
  int main() {
      createObj(); // 临时对象在表达式结束时被销毁,调用析构函数
      return 0;
  }

5. 默认析构函数 vs 自定义析构函数

如果类中没有写析构函数,编译器会自动生成一个默认析构函数。默认析构函数什么都不做(不会主动清理资源),但会自动调用成员对象的析构函数(如果有)。

示例:默认析构函数

class Inner {
public:
    ~Inner() {
        cout << "Inner类对象被销毁了" << endl;
    }
};

class Outer {
private:
    Inner inner; // Outer类的成员对象
public:
    Outer() {
        cout << "Outer类对象创建了" << endl;
    }
    // 没有写Outer的析构函数,编译器会生成默认析构函数
};

int main() {
    Outer outer; // 创建Outer对象
    return 0; // Outer对象离开作用域,默认析构函数被调用,同时调用Inner的析构函数
}

输出结果

Outer类对象创建了
Inner类对象被销毁了

6. 注意事项

  • 不能显式调用析构函数:C++不允许手动调用析构函数(如 obj.~MyClass();),否则会导致重复调用,引发未定义行为。
  • 不能重载析构函数:因为没有参数,无法通过参数区分不同的“初始化方式”,所以析构函数只有一个。
  • 虚析构函数的重要性:如果基类指针指向派生类对象,且基类析构函数不是虚函数,delete 基类指针时可能只调用基类析构函数,导致派生类资源无法清理。此时需将基类析构函数声明为 virtual(虚析构函数)。

示例:虚析构函数

class Base {
public:
    virtual ~Base() { // 虚析构函数
        cout << "Base析构函数调用" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived析构函数调用" << endl;
    }
};

int main() {
    Base* p = new Derived(); // 基类指针指向派生类对象
    delete p; // 正确调用:先Derived析构,再Base析构
    return 0;
}

输出结果

Derived析构函数调用
Base析构函数调用

总结

析构函数是C++中对象“生命终点”的清理工具,核心作用是:
- 释放动态分配的内存(避免内存泄漏);
- 关闭打开的文件或网络连接;
- 清理其他临时资源。

记住:对象销毁时,析构函数会自动调用,无需手动触发。合理使用析构函数是避免资源浪费和内存泄漏的关键!

Xiaoye