Menu
CoddyTech

深入了解析构函数

本课属于 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 离开作用域,调用了析构函数

与构造函数不同,一个类只能有一个析构函数。你不能重载它。

如果你不定义析构函数,编译器会生成一个默认析构函数,它不会做任何特别的事,只会销毁每个成员。对于管理资源的类,你必须自己编写析构函数,以防止内存泄漏。

challenge icon

挑战

简单

让我们构建一个会话管理器,用来跟踪活跃的用户会话,并演示对象被销毁时析构函数如何自动清理资源,无论对象是离开作用域还是被显式删除。

你将创建两个文件来组织代码:

  • 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,使用输入的用户名、会话 ID 101 和输入的日志大小。记录两项活动,代码分别为 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;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器