C++ 中的 RAII
本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 16 课,共 104 课。
RAII(资源获取即初始化)是 C++ 中一种基本惯用法,它将资源管理与对象的生命周期绑定在一起。原理很简单:在构造函数中获取资源,在析构函数中释放资源。
创建对象时,它会获取所需的资源。对象超出作用域时,其析构函数会自动释放这些资源。即使发生异常,也能确保完成清理。
class FileHandler {
std::FILE* file;
public:
FileHandler(const char* filename) {
file = std::fopen(filename, "r"); // 获取资源
}
~FileHandler() {
if (file) std::fclose(file); // 释放资源
}
};使用 RAII,你不会忘记清理资源:
void processFile() {
FileHandler handler("data.txt"); // 已获取资源
// 处理文件……
// 即使这里抛出异常,析构函数仍会运行!
} // 资源自动释放智能指针是 RAII 实际应用的绝佳例子。unique_ptr 在构造函数中获取堆内存的所有权,并在析构函数中删除它。你已经在使用 RAII 了,只是之前没有意识到!
RAII 适用于任何资源:内存、文件句柄、网络连接、互斥锁或数据库连接。通过将资源封装在对象中,C++ 可以确保进行确定性清理,并消除泄漏和悬空句柄等整类错误。
挑战
简单让我们构建一个连接管理器,通过自动管理类似网络连接的生命周期来演示 RAII。创建连接时,它应该“打开”;连接超出作用域时,它应该自动“关闭”,无需手动清理!
你将创建两个文件来组织代码:
Connection.h:定义一个使用 RAII 原则管理连接资源的Connection类。这个类应包含:- 一个私有
connectionId属性(字符串),用于标识连接 - 一个私有
isOpen属性(bool),用于跟踪连接状态 - 一个接收连接 ID 的构造函数,将
isOpen设为 true,并打印"Opening connection: <id>" - 一个析构函数,检查连接是否处于打开状态,将
isOpen设为 false 来关闭连接,并打印"Closing connection: <id>" - 一个
send(string message)方法,在连接处于打开状态时打印"[<id>] Sending: <message>" - 一个返回连接 ID 的
getId()方法
- 一个私有
main.cpp:通过从输入中读取连接 ID 和消息,演示 RAII 如何确保自动清理。然后:- 使用花括号
{ }创建一个作用域 - 在该作用域内,使用输入的 ID 在栈上创建一个
Connection对象 - 使用这个连接发送输入的消息
- 让作用域结束(连接应自动关闭)
- 在作用域结束后,打印
"Connection out of scope"
- 使用花括号
RAII 的优点在于,你不需要记得关闭连接:对象离开作用域时,析构函数会自动处理。这种模式可以防止资源泄漏,即使代码中发生意外情况也是如此。
在 main.cpp 中使用 #include "Connection.h" 引入头文件。
亲手试试
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
string connectionId;
string message;
// 读取输入
cin >> connectionId;
cin.ignore();
getline(cin, message);
// TODO: 用花括号 { } 创建一个作用域
// 在作用域内:
// - 用 connectionId 在栈上创建一个 Connection 对象
// - 使用连接发送消息
// 让作用域结束(析构函数会自动调用)
// TODO: 作用域结束后,输出 "Connection out of scope"
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器