文档目录

一、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
}