深入了解析构函数
本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 24 课,共 104 课。
析构函数是一种特殊的成员函数,会在对象被销毁时自动运行。它的主要作用是释放对象在生命周期内获取的资源,例如动态分配的内存、文件句柄或网络连接。
析构函数的名称与类相同,前面加上波浪号 ~。它不接受任何参数,也没有返回类型:
class FileHandler {
std::string filename;
int* buffer;
public:
FileHandler(std::string name, size_t size)
: filename(name), buffer(new int[size]) {
std::cout << "Opening " << filename << "\n";
}
~FileHandler() {
delete[] buffer;
std::cout << "Closing " << filename << "\n";
}
};在以下情况下会自动调用析构函数:
- 栈对象超出作用域
- 对堆上分配的对象调用
delete - 临时对象的生命周期结束
void example() {
FileHandler f1("data.txt", 100); // 调用了构造函数
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // 调用了 f2 的析构函数
} // f1 离开作用域,调用了析构函数与构造函数不同,一个类只能有一个析构函数。你不能重载它。
如果你不定义析构函数,编译器会生成一个默认析构函数,它不会做任何特别的事,只会销毁每个成员。对于管理资源的类,你必须自己编写析构函数,以防止内存泄漏。
挑战
简单让我们构建一个会话管理器,用来跟踪活跃的用户会话,并演示对象被销毁时析构函数如何自动清理资源,无论对象是离开作用域还是被显式删除。
你将创建两个文件来组织代码:
Session.h:定义一个Session类,用来表示包含动态分配数据的活跃用户会话。你的类应包含:- 私有成员:一个
username(字符串)、一个sessionId(int),以及一个名为activityLog的整数数组指针,用来跟踪用户操作;还要包含用于表示数组大小的logSize - 一个构造函数,接收用户名、会话 ID 和日志大小作为参数。它应在堆上分配活动日志数组,将所有元素初始化为 0,并打印
"Session <sessionId> started for <username>" - 一个析构函数,用来释放已分配的内存并打印
"Session <sessionId> ended for <username>" - 一个
logActivity(int activityCode)方法,用来将活动代码存储在下一个可用位置(在内部跟踪当前位置);如果日志已满,则忽略此次调用,因此计数不会超过日志大小 - 一个
getActivityCount()方法,用来返回已记录的活动数量 - 一个
getUsername()方法,用来返回用户名
- 私有成员:一个
main.cpp:演示不同情况下析构函数的行为。从输入中读取用户名和日志大小(分别位于单独的行),然后:- 使用花括号
{ }创建一个作用域块,并在其中创建一个栈分配的Session,使用输入的用户名、会话 ID101和输入的日志大小。记录两项活动,代码分别为1和2,然后打印"Stack session activities: <count>"。代码块结束时,析构函数应自动运行。 - 代码块结束后,打印
"Stack session destroyed" - 使用
new创建一个堆分配的Session,用户名为"Guest",会话 ID 为202,日志大小为3。记录一项活动,代码为5,打印"Heap session activities: <count>",然后显式地使用delete删除它。 - 删除后,打印
"Heap session destroyed"
- 使用花括号
这个挑战展示了调用析构函数的两种主要方式:栈对象离开作用域时自动调用,以及删除堆对象时手动调用。这两种方式都应正确释放动态分配的活动日志,以防止内存泄漏。
在 main.cpp 中使用 #include "Session.h" 引入头文件。
亲手试试
#include <iostream>
#include <string>
#include "Session.h"
using namespace std;
int main() {
// 读取输入
string username;
int logSize;
getline(cin, username);
cin >> logSize;
// TODO:用花括号 { } 创建一个作用域块
// 在块内:
// 创建一个在栈上分配的 Session,使用输入的用户名、会话 ID 101 和输入的日志大小
// 记录两个活动,代码分别为 1 和 2
// 打印 "Stack session activities: <count>"
// 块结束时,析构函数会自动运行
// TODO:块结束后,打印 "Stack session destroyed"
// TODO:使用 new 创建一个在堆上分配的 Session
// 用户名:"Guest",会话 ID:202,日志大小:3
// 记录一个代码为 5 的活动
// 打印 "Heap session activities: <count>"
// 删除该会话
// TODO:删除后,打印 "Heap session destroyed"
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器