C++ Academy · 课时

作用域守卫

在退出作用域时执行清理

第 4 / 4 课13 个步骤

作用域守卫 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。

什么是作用域守卫

作用域守卫是一种 RAII 对象,其唯一任务是在离开作用域时执行清理操作——即使函数提前返回或抛出异常也不例外。

它解决的问题

如果没有守卫,在提前执行 return 或 throw 后很容易忘记清理。作用域守卫会让这项操作自动执行。

lock_guard:标准作用域守卫

std::lock_guard 会在构造时锁定互斥量,并在析构时解锁——它是专门用于锁的内置作用域守卫。

#include <mutex>

std::mutex m;

void critical() {
    std::lock_guard<std::mutex> lk(m);   // locked
    // ... work ...
}                                        // unlocked automatically

手动编写守卫

您可以编写一个持有函数并在析构函数中调用它的守卫。

#include <iostream>
#include <functional>

class ScopeGuard {
    std::function<void()> fn;
public:
    ScopeGuard(std::function<void()> f) : fn(std::move(f)) {}
    ~ScopeGuard() { fn(); }
};

int main() {
    ScopeGuard g([] { std::cout << "cleanup\n"; });
    std::cout << "work\n";
}

提前返回时执行清理

由于析构函数会在退出作用域时触发,无论函数以何种方式退出,守卫的操作都会执行。

#include <iostream>
#include <functional>

class ScopeGuard {
    std::function<void()> fn;
public:
    ScopeGuard(std::function<void()> f) : fn(std::move(f)) {}
    ~ScopeGuard() { fn(); }
};

void run(bool stop) {
    ScopeGuard g([] { std::cout << "always cleaned\n"; });
    if (stop) return;
}

int main() { run(true); }

发生异常时执行清理

如果异常沿作用域向外传播,守卫的析构函数仍会运行,从而安全地释放资源。

取消守卫

有时您希望跳过清理(因为操作已经成功)。真正的守卫会提供 dismiss() 来禁用这项操作。

#include <iostream>
#include <functional>

class ScopeGuard {
    std::function<void()> fn;
    bool active = true;
public:
    ScopeGuard(std::function<void()> f) : fn(std::move(f)) {}
    void dismiss() { active = false; }
    ~ScopeGuard() { if (active) fn(); }
};

int main() {
    ScopeGuard g([] { std::cout << "rollback\n"; });
    g.dismiss();   // success: no rollback
}

用于回滚

作用域守卫非常适合事务性代码:注册回滚操作,执行工作,然后在成功时取消守卫。

守卫只能移动

守卫不应被拷贝——拷贝会导致该操作执行两次。请让守卫只能移动或不可拷贝。

标准库和辅助工具

std::scoped_lock 可以保护多个互斥量,而 GSL 等库提供了 finally。许多代码库还会提供一个小型的 ScopeGuard 工具。

优先使用专用 RAII 类型

当存在专用的 RAII 类型时(如 lock_guard、unique_ptr、fstream),请优先使用它。只有在没有自然封装方式的临时清理场景中,才使用通用作用域守卫。

快速检查

请测试您对作用域守卫的理解。

回顾

您已经学习了作用域守卫如何通过 RAII 在退出作用域时自动执行清理。它可以处理提前返回和异常,支持通过 dismiss() 执行回滚,并且应只能移动。存在专用 RAII 类型时,请优先使用它们。

免费开始

用 AI 导师学习 C++ — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「作用域守卫」课时是免费的吗?

是的 — 「作用域守卫」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。

「作用域守卫」这节课中我会学到什么?

在退出作用域时执行清理 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C++ Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「作用域守卫」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C++ Academy 课中编写并运行代码吗?

能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. RAII 原则
  2. 析构函数与清理
  3. 三法则与五法则
  4. 作用域守卫
← 返回 C++ Academy