目录
引言
类似于前面的构造函数,想要掌握析构函数,也应该从这两个方面入手:
析构函数的特性
-
名称:析构函数的名称必须与类名相同,但前面有一个波浪号(
~
)。例如,类MyClass
的析构函数名称为~MyClass()
。 -
无返回类型:析构函数没有返回类型,包括
void
。 -
无参数:析构函数不接受任何参数。
-
自动调用:当一个对象的生命周期结束时,析构函数会被自动调用。这包括以下几种情况:
- 局部对象离开其作用域时。
- 动态分配的对象被
delete
释放时。 - 全局或静态对象在程序结束时。
- 对象作为类的成员被销毁时(例如,当包含该对象的另一个对象被销毁时)
默认析构函数的行为:
如果类没有显式定义析构函数,编译器会生成一个默认析构函数。默认析构函数对于内置类型不做处理,对于自定义类型成员会调⽤他的析构函数·。
还需要注意的是:
我们显式写析构函数,对于⾃定义类型成员也会调用他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数
析构函数的规则
-
只能有一个析构函数:每个类只能有一个析构函数。
-
不能被重载:析构函数不能被重载,因为它们的名称和参数列表是固定的。这一点不同于构造函数
-
不能被显式调用:析构函数不能被显式调用(除了通过
delete
释放动态分配的对象)。它们是由编译器自动调用的。 -
对象销毁顺序:局部对象的销毁顺序与它们的创建顺序相反。全局和静态对象的销毁顺序与它们的定义顺序相反(但具体顺序在不同编译器和链接器之间可能有所不同)。
-
成员对象的析构:当一个对象被销毁时,它的成员对象(包括基类部分)的析构函数会按照它们被构造的相反顺序被调用。
需要自己实现析构函数的情况:
- 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数
- 如果默认⽣成的析构就可以用,也就不需要显式写析构,比如类中的成员变量都为自定义类型,那么默认析构函数会自动调用成员变量的析构函数
- 有资源申请时,⼀定要自己写析构,否则会造成资源泄漏
示例代码:
以下是一个简单的C++类,它包含动态分配的内存,并在析构函数中释放这些内存:
#include <iostream>
class MyClass {
private:
int* data; // 动态分配的内存
public:
// 构造函数,动态分配内存
MyClass(int size) {
data = new int[size];
std::cout << "Memory allocated for MyClass object." << std::endl;
}
// 析构函数,释放动态分配的内存
~MyClass() {
delete[] data;
std::cout << "Memory freed for MyClass object." << std::endl;
}
// 其他成员函数...
};
int main() {
MyClass obj(10); // 创建对象,分配内存
// ... 使用对象 ...
// 当obj离开作用域时,析构函数被调用,释放内存
return 0;
}
实现析构函数需要考虑的问题:
析构函数的实现通常涉及以下步骤:
- 释放动态内存:如果对象在生命周期中动态分配了内存,析构函数应该负责释放这些内存。
- 关闭文件和网络连接:如果对象持有文件句柄或网络连接,析构函数应该负责关闭它们。
- 调用成员对象的析构函数:如果对象包含其他对象作为成员,析构函数将自动调用这些成员对象的析构函数(按照成员变量在类中声明的逆序)。注意是自动调用,无需也无法主动调用
- 执行其他必要的清理操作:如解除锁、记录日志等。
结尾
C++析构函数是对象生命周期管理的重要组成部分。通过正确地实现和使用析构函数,程序员可以确保资源得到正确的释放和管理,从而避免内存泄漏和其他资源相关问题。
理解析构函数的工作原理和最佳实践对于编写健壮、可维护的C++代码至关重要。