C++中的析构函数
析构函数用于对象销毁时的资源清理函数名是~类名,无参数,无返回值,不能重载自动调用(栈对象)或配合delete手动调用(堆对象)基类作为多态基类时,应声明虚析构函数是 RAII 编程思想的核心机制。
·
1. 析构函数的作用
析构函数是一种特殊的成员函数,它在对象生命周期结束时自动调用,主要用来:
- 释放对象在生命周期内申请的资源(如动态内存、文件句柄、网络连接、互斥锁等)
- 执行一些清理工作,确保资源被正确释放,防止内存泄漏
2. 析构函数的特点
- 函数名:在类名前加
~
(波浪号) - 没有参数,也没有返回值类型(连
void
都不能写)(重点要记住,没有形参,没有返回值) - 不能重载(一个类只能有一个析构函数)(注意和构造函数可以存在多个和重载的区别)
- 声明为
public
(通常情况下),以便编译器能在对象销毁时自动调用 - 如果用户没有定义析构函数,编译器会自动生成一个默认析构函数(空实现)
3. 基本语法示例
cpp
运行
#include <iostream>
using namespace std;
class Person {
public:
string name;
// 构造函数
Person(string n) {
name = n;
cout << "构造函数: " << name << " 被创建\n";
}
// 析构函数
~Person() {
cout << "析构函数: " << name << " 被销毁\n";
}
};
int main() {
Person p1("Alice"); // 创建对象,调用构造函数
{
Person p2("Bob"); // 作用域内创建
} // p2 生命周期结束,调用析构函数
cout << "main 函数即将结束\n";
} // p1 生命周期结束,调用析构函数
运行结果:
plaintext
构造函数: Alice 被创建
构造函数: Bob 被创建
析构函数: Bob 被销毁
main 函数即将结束
析构函数: Alice 被销毁
4. 析构函数的调用时机
析构函数自动调用,调用时机取决于对象的存储方式:
这句话又该怎么理解
4.1 栈上的局部对象
- 当对象所在的作用域结束时,自动调用析构函数
cpp运行
void func() {
Person p("Tom");
} // 离开 func() 时,p 的析构函数被调用
4.2 堆上的动态对象
- 使用
new
创建的对象,必须用delete
手动释放,delete
会调用析构函数
cpp运行
Person* p = new Person("Jerry");
delete p; // 必须手动释放,否则会内存泄漏
4.3 静态对象
- 在程序结束时,才调用析构函数
cpp运行
void func() {
static Person p("Static");
} // 函数结束时不会销毁,直到程序退出时才调用析构函数
5. 析构函数与资源管理(RAII)
析构函数是 RAII(资源获取即初始化) 编程思想的核心:
- 在构造函数中获取资源
- 在析构函数中释放资源
- 这样可以保证资源一定会被释放,即使发生异常
cpp
运行
#include <iostream>
using namespace std;
class FileHandler {
private:
FILE* fp;
public:
FileHandler(const char* filename) {
fp = fopen(filename, "w");
if (!fp) cout << "文件打开失败\n";
else cout << "文件已打开\n";
}
~FileHandler() {
if (fp) {
fclose(fp);
cout << "文件已关闭\n";
}
}
};
int main() {
FileHandler fh("test.txt");
// ... 使用文件
} // 自动调用析构函数关闭文件
6. 虚析构函数(Virtual Destructor)
当使用基类指针指向派生类对象时,如果基类析构函数不是虚函数,delete
基类指针时只会调用基类析构函数,不会调用派生类析构函数,造成资源泄漏。
cpp
运行
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base 构造\n"; }
virtual ~Base() { cout << "Base 析构\n"; } // 虚析构函数
};
class Derived : public Base {
public:
Derived() { cout << "Derived 构造\n"; }
~Derived() { cout << "Derived 析构\n"; }
};
int main() {
Base* p = new Derived(); // 基类指针指向派生类对象
delete p; // 如果基类析构是虚函数,会先调用 Derived 析构,再调用 Base 析构
}
运行结果(虚析构):
plaintext
Base 构造
Derived 构造
Derived 析构
Base 析构
结论:
- 如果类可能被继承,且可能通过基类指针删除派生类对象,基类的析构函数应该声明为
virtual
7. 析构函数的调用顺序
- 对于局部对象:按构造的相反顺序调用析构函数(后进先出)
- 对于成员对象:先构造成员对象,再构造自身;析构时相反
- 对于继承:先调用派生类析构,再调用基类析构
8. 总结要点
- 析构函数用于对象销毁时的资源清理
- 函数名是
~类名
,无参数,无返回值,不能重载 - 自动调用(栈对象)或配合
delete
手动调用(堆对象) - 基类作为多态基类时,应声明虚析构函数
- 是 RAII 编程思想的核心机制
更多推荐
所有评论(0)