跳转至

嵌入式现代C++教程——作用域守卫(Scope Guard):让清理代码乖乖在"出门顺手关灯"那一刻执行

写嵌入式代码时,总会遇到这样的人生真相:你在函数某处申请了资源(打开外设、上锁、禁中断、分配缓冲……),后来代码分叉、提前 return、甚至抛出异常——结果忘了释放/恢复。结果就是内存泄漏、死锁、外设状态奇怪,或者你被老大盯着问"为什么这段代码跑了两分钟还没返回"。

作用域守卫(Scope Guard)就是为了解决这个问题的——把"离开当前作用域时必须做的事"绑定在一个对象的析构函数上:只要对象离开作用域,析构函数就会执行,清理也就稳了。它是 RAII 的小而美的实用变体,尤其适合嵌入式场景(没有堆分配、追求确定性)。


先看最简单的:lambda + 小模板

这是最常见的现代 C++ 写法(C++11 起就能用)。核心思想:封装一个可调用对象,析构时调用它(如果没有被取消)。

#include <utility>
#include <exception>
#include <cstdlib> // for std::terminate

template <typename F>
class ScopeGuard {
public:
    explicit ScopeGuard(F&& f) noexcept
        : fn_(std::move(f)), active_(true) {}

    // 不允许拷贝(避免重复调用)
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;

    // 允许移动
    ScopeGuard(ScopeGuard&& other) noexcept
        : fn_(std::move(other.fn_)), active_(other.active_) {
        other.dismiss();
    }

    ~ScopeGuard() noexcept {
        if (active_) {
            try {
                fn_();
            } catch (...) {
                // 析构函数不可抛出 —— 在嵌入式中通常直接终止
                std::terminate();
            }
        }
    }

    void dismiss() noexcept { active_ = false; }

private:
    F fn_;
    bool active_;
};

// 辅助函数方便模板推导
template <typename F>
ScopeGuard<F> make_scope_guard(F&& f) {
    return ScopeGuard<F>(std::forward<F>(f));
}
查看完整可编译示例
// Scope Guard 作用域守卫示例
// 演示如何使用作用域守卫确保清理代码在作用域结束时执行

#include <utility>
#include <cstdio>
#include <cstdlib>

// ========== 基础 ScopeGuard 实现 ==========

template <typename F>
class ScopeGuard {
public:
    explicit ScopeGuard(F&& f) noexcept
        : func_(std::move(f)), active_(true) {}

    ~ScopeGuard() noexcept {
        if (active_) {
            func_();
        }
    }

    // 不允许拷贝(避免重复调用)
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;

    // 允许移动
    ScopeGuard(ScopeGuard&& other) noexcept
        : func_(std::move(other.func_)), active_(other.active_) {
        other.dismiss();
    }

    void dismiss() noexcept { active_ = false; }

private:
    F func_;
    bool active_;
};

template <typename F>
ScopeGuard<typename std::decay<F>::type> make_scope_guard(F&& f) {
    return ScopeGuard<typename std::decay<F>::type>(std::forward<F>(f));
}

// ========== 辅助宏 ==========

#define CONCAT_IMPL(x, y) x##y
#define CONCAT(x, y) CONCAT_IMPL(x, y)
#define SCOPE_GUARD(code) \
    auto CONCAT(_scope_guard_, __COUNTER__) = make_scope_guard([&](){ code; })

// ========== 资源管理模拟 ==========

namespace res {
    inline void lock() {
        printf("[Resource] Locked\n");
    }

    inline void unlock() {
        printf("[Resource] Unlocked\n");
    }

    inline void acquire() {
        printf("[Resource] Acquired\n");
    }

    inline void release() {
        printf("[Resource] Released\n");
    }

    inline void enable_irq() {
        printf("[IRQ] Enabled\n");
    }

    inline void disable_irq() {
        printf("[IRQ] Disabled\n");
    }
}

// ========== 使用示例 ==========

void basic_example() {
    printf("=== Basic ScopeGuard Example ===\n");

    res::acquire();
    auto guard = make_scope_guard([]{ res::release(); });

    printf("Doing work...\n");

    // 任何 return 都会触发 guard
    if (true) {
        printf("Early return\n");
        return;
    }

    guard.dismiss();  // 不会到达这里
}

void multiple_guards() {
    printf("\n=== Multiple Guards Example ===\n");

    res::acquire();
    auto release_guard = make_scope_guard([]{ res::release(); });

    res::lock();
    auto unlock_guard = make_scope_guard([]{ res::unlock(); });

    printf("Critical section...\n");

    // 以相反顺序销毁(RAII 特性)
}

void dismiss_example() {
    printf("\n=== Dismiss Example ===\n");

    res::acquire();
    auto guard = make_scope_guard([]{ res::release(); });

    printf("Operation succeeded\n");

    guard.dismiss();  // 取消清理
    printf("Guard dismissed\n");
}

void macro_example() {
    printf("\n=== Macro Example ===\n");

    res::lock();
    SCOPE_GUARD({ res::unlock(); });

    printf("In critical section (using macro)\n");
}

// ========== 实际应用场景 ==========

void register_restore_example() {
    printf("\n=== Register Restore Example ===\n");

    // 模拟寄存器
    static uint32_t REG_CTRL = 0x1000;
    uint32_t old_value = REG_CTRL;

    SCOPE_GUARD({ REG_CTRL = old_value; });

    printf("Old register value: 0x%X\n", old_value);

    // 修改寄存器
    REG_CTRL = 0x2000;
    printf("New register value: 0x%X\n", REG_CTRL);

    printf("Doing work with modified register\n");

    // 离开作用域时自动恢复
    printf("After scope, register restored to: 0x%X\n", REG_CTRL);
}

void irq_save_restore_example() {
    printf("\n=== IRQ Save/Restore Example ===\n");

    // 模拟中断状态
    static bool irq_enabled = true;
    bool saved_irq = irq_enabled;

    // 禁用中断
    irq_enabled = false;
    printf("IRQ disabled\n");

    SCOPE_GUARD({ irq_enabled = saved_irq; printf("IRQ restored\n"); });

    printf("In critical section with IRQ disabled\n");

    // 离开作用域时自动恢复
}

// ========== 异常分支(概念示例)==========

// 注意:如果项目禁用异常,这些示例主要用于说明概念
// 在实际嵌入式代码中,通常用返回值表示错误

void error_handling_example() {
    printf("\n=== Error Handling Example ===\n");

    res::acquire();
    auto cleanup = make_scope_guard([]{ res::release(); });

    bool operation_failed = false;

    if (operation_failed) {
        printf("Operation failed, cleanup will execute\n");
        return;  // cleanup 仍会执行
    }

    printf("Operation succeeded\n");
    cleanup.dismiss();  // 成功时取消清理
}

// ========== 嵌套作用域 ==========

void nested_scope_example() {
    printf("\n=== Nested Scope Example ===\n");

    auto outer = make_scope_guard([]{ printf("Outer cleanup\n"); });

    {
        auto inner = make_scope_guard([]{ printf("Inner cleanup\n"); });
        printf("Inside nested scope\n");
    }

    printf("Back in outer scope\n");
}

// ========== 条件性清理 ==========

void conditional_cleanup_example() {
    printf("\n=== Conditional Cleanup Example ===\n");

    bool transaction_active = true;
    auto rollback = make_scope_guard([]{
        printf("Transaction rolled back\n");
    });

    printf("Transaction started\n");

    // 模拟成功
    bool success = true;

    if (success) {
        printf("Transaction committing\n");
        rollback.dismiss();  // 成功,不需要回滚
    }
}

// ========== 状态切换示例 ==========

class StateMachine {
public:
    enum State { Idle, Busy, Error };

    void enter_busy() {
        old_state_ = current_state_;
        current_state_ = Busy;
        printf("State: %s -> Busy\n", to_string(old_state_));

        // 确保退出时恢复状态
        SCOPE_GUARD({ current_state_ = old_state_;
                     printf("State restored to %s\n", to_string(current_state_)); });

        printf("Doing busy work...\n");
        // 离开作用域时自动恢复
    }

private:
    State current_state_ = Idle;
    State old_state_;

    const char* to_string(State s) {
        switch (s) {
            case Idle: return "Idle";
            case Busy: return "Busy";
            case Error: return "Error";
        }
        return "Unknown";
    }
};

void state_machine_example() {
    printf("\n=== State Machine Example ===\n");

    StateMachine sm;
    sm.enter_busy();
}

int main() {
    basic_example();
    multiple_guards();
    dismiss_example();
    macro_example();
    register_restore_example();
    irq_save_restore_example();
    error_handling_example();
    nested_scope_example();
    conditional_cleanup_example();
    state_machine_example();

    printf("\n=== All Examples Complete ===\n");
    printf("\nKey Takeaways:\n");
    printf("- ScopeGuard ensures cleanup code runs on all exit paths\n");
    printf("- No heap allocation, suitable for embedded\n");
    printf("- dismiss() allows cancelling cleanup when not needed\n");
    printf("- Guards execute in reverse order of construction (RAII)\n");

    return 0;
}

用法示例:

void foo() {
    auto g = make_scope_guard([](){ close_device(); });
    // do something...
    if (error) return; // close_device 会被保证调用
    g.dismiss(); // 如果想提前取消清理
}

幽默注:dismiss() 就是给守卫放假,不让它在离职那天烦你。


成功/失败分支:scope_successscope_fail

有时候你只想在函数"正常返回"(no exception)时做事,或者只在抛异常时处理。C++17 提供了 std::uncaught_exceptions() 来判断析构时是否处于异常传播中。基于它,我们可以实现 scope_exit(总是执行)、scope_success(仅在没有异常时执行)、scope_fail(仅在有异常时执行)。

#include <exception>

template <typename F>
class ScopeGuardOnExit {
    // 同上,始终执行
};

template <typename F>
class ScopeGuardOnSuccess {
public:
    explicit ScopeGuardOnSuccess(F&& f) noexcept
      : fn_(std::move(f)), active_(true), uncaught_at_construction_(std::uncaught_exceptions()) {}

    ~ScopeGuardOnSuccess() noexcept {
        if (active_ && std::uncaught_exceptions() == uncaught_at_construction_) {
            try { fn_(); } catch(...) { std::terminate(); }
        }
    }
    // ... move/dismiss same as above
private:
    F fn_;
    bool active_;
    int uncaught_at_construction_;
};

template <typename F>
class ScopeGuardOnFail {
public:
    explicit ScopeGuardOnFail(F&& f) noexcept
      : fn_(std::move(f)), active_(true), uncaught_at_construction_(std::uncaught_exceptions()) {}

    ~ScopeGuardOnFail() noexcept {
        if (active_ && std::uncaught_exceptions() > uncaught_at_construction_) {
            try { fn_(); } catch(...) { std::terminate(); }
        }
    }
    // ...
private:
    F fn_;
    bool active_;
    int uncaught_at_construction_;
};

这样你就可以写:

auto on_success = make_scope_guard_success([](){ commit_tx(); });
auto on_fail = make_scope_guard_fail([](){ rollback_tx(); });

在嵌入式里如果禁用异常,这俩就没用武之地 —— 但是 scope_exit(总是执行)仍然非常有用。

查看完整可编译示例
// ScopeGuard with success/fail branches
// 需要 C++17 的 std::uncaught_exceptions()

#include <utility>
#include <cstdio>
#include <exception>

// ========== 基础 ScopeExit (始终执行) ==========

template <typename F>
class ScopeExit {
public:
    explicit ScopeExit(F&& f) noexcept
        : func_(std::move(f)), active_(true) {}

    ~ScopeExit() noexcept {
        if (active_) {
            func_();
        }
    }

    ScopeExit(const ScopeExit&) = delete;
    ScopeExit& operator=(const ScopeExit&) = delete;

    ScopeExit(ScopeExit&& other) noexcept
        : func_(std::move(other.func_)), active_(other.active_) {
        other.active_ = false;
    }

    void dismiss() noexcept { active_ = false; }

private:
    F func_;
    bool active_;
};

template <typename F>
ScopeExit<typename std::decay<F>::type> make_scope_exit(F&& f) {
    return ScopeExit<typename std::decay<F>::type>(std::forward<F>(f));
}

// ========== ScopeSuccess (仅无异常时执行) ==========

template <typename F>
class ScopeSuccess {
public:
    explicit ScopeSuccess(F&& f) noexcept
        : func_(std::move(f))
        , active_(true)
        , uncaught_at_construction_(std::uncaught_exceptions()) {}

    ~ScopeSuccess() noexcept {
        if (active_ && std::uncaught_exceptions() == uncaught_at_construction_) {
            func_();
        }
    }

    ScopeSuccess(const ScopeSuccess&) = delete;
    ScopeSuccess& operator=(const ScopeSuccess&) = delete;

    ScopeSuccess(ScopeSuccess&& other) noexcept
        : func_(std::move(other.func_))
        , active_(other.active_)
        , uncaught_at_construction_(other.uncaught_at_construction_) {
        other.active_ = false;
    }

    void dismiss() noexcept { active_ = false; }

private:
    F func_;
    bool active_;
    int uncaught_at_construction_;
};

template <typename F>
ScopeSuccess<typename std::decay<F>::type> make_scope_success(F&& f) {
    return ScopeSuccess<typename std::decay<F>::type>(std::forward<F>(f));
}

// ========== ScopeFail (仅异常时执行) ==========

template <typename F>
class ScopeFail {
public:
    explicit ScopeFail(F&& f) noexcept
        : func_(std::move(f))
        , active_(true)
        , uncaught_at_construction_(std::uncaught_exceptions()) {}

    ~ScopeFail() noexcept {
        if (active_ && std::uncaught_exceptions() > uncaught_at_construction_) {
            func_();
        }
    }

    ScopeFail(const ScopeFail&) = delete;
    ScopeFail& operator=(const ScopeFail&) = delete;

    ScopeFail(ScopeFail&& other) noexcept
        : func_(std::move(other.func_))
        , active_(other.active_)
        , uncaught_at_construction_(other.uncaught_at_construction_) {
        other.active_ = false;
    }

    void dismiss() noexcept { active_ = false; }

private:
    F func_;
    bool active_;
    int uncaught_at_construction_;
};

template <typename F>
ScopeFail<typename std::decay<F>::type> make_scope_fail(F&& f) {
    return ScopeFail<typename std::decay<F>::type>(std::forward<F>(f));
}

// ========== 使用示例 ==========

// 模拟事务管理器
class Transaction {
public:
    void begin() {
        printf("[Transaction] BEGIN\n");
    }

    void commit() {
        printf("[Transaction] COMMIT\n");
    }

    void rollback() {
        printf("[Transaction] ROLLBACK\n");
    }
};

void success_fail_example() {
    printf("=== ScopeSuccess / ScopeFail Example ===\n");

    Transaction tx;

    tx.begin();

    auto on_success = make_scope_success([&]{ tx.commit(); });
    auto on_fail = make_scope_fail([&]{ tx.rollback(); });

    printf("Transaction in progress...\n");

    // 模拟成功(没有抛出异常)
    // on_success 会执行,on_fail 不会
}

void with_exception_example() {
    printf("\n=== With Exception Example ===\n");

    Transaction tx;

    tx.begin();

    auto on_success = make_scope_success([&]{ tx.commit(); });
    auto on_fail = make_scope_fail([&]{ tx.rollback(); });

    printf("About to throw exception...\n");

    // 注意:在实际编译时需要启用异常
    // throw std::runtime_error("Something went wrong");

    // 如果异常被抛出:
    // - on_success 不会执行(因为 uncaught_exceptions 增加)
    // - on_fail 会执行
}

// 数据库操作示例
struct Database {
    void connect() { printf("[DB] Connected\n"); }
    void disconnect() { printf("[DB] Disconnected\n"); }
    void begin_tx() { printf("[DB] TX BEGIN\n"); }
    void commit_tx() { printf("[DB] TX COMMIT\n"); }
    void rollback_tx() { printf("[DB] TX ROLLBACK\n"); }
};

void database_operation(Database& db) {
    printf("\n=== Database Operation Example ===\n");

    db.connect();
    auto disconnect = make_scope_exit([&]{ db.disconnect(); });

    db.begin_tx();

    auto on_success = make_scope_success([&]{ db.commit_tx(); });
    auto on_fail = make_scope_fail([&]{ db.rollback_tx(); });

    printf("Executing query...\n");

    // 如果这里抛出异常,on_fail 会执行回滚
    // 否则 on_success 会执行提交
}

// 文件写入示例
struct FileWriter {
    void open() { printf("[File] Opened\n"); }
    void close() { printf("[File] Closed\n"); }
    void write(const char* data) { printf("[File] Writing: %s\n", data); }
    void commit() { printf("[File] Committing changes\n"); }
    void revert() { printf("[File] Reverting changes\n"); }
};

void file_write_example() {
    printf("\n=== File Write Example ===\n");

    FileWriter writer;

    writer.open();
    auto close = make_scope_exit([&]{ writer.close(); });

    auto on_success = make_scope_success([&]{ writer.commit(); });
    auto on_fail = make_scope_fail([&]{ writer.revert(); });

    writer.write("Header data");
    writer.write("Body data");
    writer.write("Footer data");

    printf("Write completed successfully\n");
}

// 状态恢复示例
class SystemState {
public:
    void save() { printf("[State] Saving current state\n"); }
    void restore() { printf("[State] Restoring saved state\n"); }
    void clear_saved() { printf("[State] Clearing saved state\n"); }
};

void risky_operation(SystemState& sys) {
    printf("\n=== Risky Operation Example ===\n");

    sys.save();

    auto on_fail = make_scope_fail([&]{ sys.restore(); });
    auto on_success = make_scope_success([&]{ sys.clear_saved(); });

    printf("Performing risky operation...\n");

    // 如果操作失败抛异常,状态会自动恢复
    // 如果成功,清除保存的状态
}

int main() {
    success_fail_example();
    with_exception_example();

    Database db;
    database_operation(db);

    file_write_example();

    SystemState sys;
    risky_operation(sys);

    printf("\n=== All Examples Complete ===\n");
    printf("\nNote: ScopeSuccess and ScopeFail require:\n");
    printf("  1. C++17 or later for std::uncaught_exceptions()\n");
    printf("  2. Exceptions enabled in compiler\n");
    printf("For embedded systems with -fno-exceptions, use ScopeExit only.\n");

    return 0;
}

方便的宏:减少样板代码

写守卫变量名挺烦的,宏可以帮你:

#define CONCAT_IMPL(x, y) x##y
#define CONCAT(x, y) CONCAT_IMPL(x, y)
#define SCOPE_GUARD(code) \
    auto CONCAT(_scope_guard_, __COUNTER__) = make_scope_guard([&](){ code; })

用法:

SCOPE_GUARD({ disable_irq(); restore_irq_state(saved); });
// 作用域结束时自动调用

在没有 __COUNTER__ 的编译器上用 __LINE__ 也行,不过 __COUNTER__ 更保险。


例子

  1. 禁用中断并保证恢复(伪代码,平台提供 __disable_irq() / __enable_irq()):
void critical_section() {
    bool prev = save_and_disable_irq();
    auto restore = make_scope_guard([&]{ restore_irq(prev); });

    // 关键操作
}
  1. 上锁/解锁
mutex.lock();
auto unlock = make_scope_guard([&]{ mutex.unlock(); });
// 如果函数中途 return,mutex 会被正确解锁
  1. 临时改变寄存器、并在退出恢复
uint32_t old = REG_CTRL;
REG_CTRL = old | ENABLE_BIT;
auto restore_reg = make_scope_guard([=]{ REG_CTRL = old; });

嵌入式注意事项与最佳实践

  • 不要分配堆内存:守卫对象本身应该在栈上,成员不要包含动态分配,嵌入式通常禁用或不喜欢堆。
  • 析构函数必须不抛异常:标准要求析构不能抛出(会导致 std::terminate 在异常传播时)。我们的实现用 try/catch(...) { std::terminate(); } 或者如果你有日志系统可以先记录再终止。另一个选择是静默吞掉异常,但那可能掩盖错误。
  • 尽量内联(inline:模板 + constexpr / inline 有利于编译器优化,不增加运行时开销。
  • 对象大小:实现非常小(一个可调用对象 + 一个 bool),对内存敏感的场景适合。避免把大对象捕获到 lambda 中。
  • 编译器/标准:如果你用的是老旧编译器,确保至少支持 C++11(lambda、移动语义)。若要 scope_success/fail,需要 C++17 的 std::uncaught_exceptions()
  • 禁用异常的环境:如果工程编译时禁用异常(-fno-exceptions),scope_fail/scope_success 不可用。scope_exit 仍然适用,仍能保证清理行为。
  • 避免在中断上下文做复杂事情:在 ISR 中创建守卫要小心(栈空间有限、不要调用可能阻塞或分配的函数)。

std::unique_ptr 的小技巧(简单场景)

有时候你只是想用现有工具来做简单清理:

#include <memory>

auto closer = std::unique_ptr<void, decltype([](void*){ close_fd(fd); })>(nullptr, [](void*){ close_fd(fd); });

但这种写法语义上不如专门的 ScopeGuard 清晰(模板更适合做任意清理),我提是为了给你多一个"武器库"里的小工具。