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++中对象“生命终点”的清理工具,核心作用是:
- 释放动态分配的内存(避免内存泄漏);
- 关闭打开的文件或网络连接;
- 清理其他临时资源。
记住:对象销毁时,析构函数会自动调用,无需手动触发。合理使用析构函数是避免资源浪费和内存泄漏的关键!