一、RAII 的本质
RAII(Resource Acquisition Is Initialization)的核心思想:资源获取和对象构造绑定,资源释放和对象析构绑定。
你每天都在用:
std::lock_guard<std::mutex> lock(mtx); // 构造时 lock,析构时 unlock
std::ifstream file("data.txt"); // 构造时 open,析构时 close
std::unique_ptr<Foo> ptr(new Foo); // 构造时 new,析构时 delete
二、ScopeGuard——通用的 RAII 封装
场景:函数中间有多个 return 点,每个 return 前都要做同样的清理:
void process() {
FILE* f = fopen("data.txt", "r");
// ... 如果这里 return 了,f 没关
if (something) return;
// ... 如果这里抛异常,f 没关
fclose(f);
}
ScopeGuard 让你在构造时注册一个清理函数,析构时自动执行:
class ScopeGuard {
std::function<void()> cleanup_;
bool active_ = true;
public:
explicit ScopeGuard(std::function<void()> cleanup)
: cleanup_(std::move(cleanup)) {}
~ScopeGuard() {
if (active_) cleanup_(); // 析构时执行清理
}
// 如果成功,可以调用 dismiss 取消清理
void dismiss() { active_ = false; }
};
void process() {
FILE* f = fopen("data.txt", "r");
ScopeGuard close_file([&] { if (f) fclose(f); });
if (something) return; // 析构 ScopeGuard → 自动 fclose
// ... 正常处理
close_file.dismiss(); // 成功,不再需要自动清理
fclose(f); // 手动关闭
}
更简洁的宏(很多 C++ 项目这么用):
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = ScopeGuard([&]()
// 使用
void process() {
FILE* f = fopen("data.txt", "r");
SCOPE_EXIT { if (f) fclose(f); };
if (something) return; // 自动 fclose
}