C++ 说:“我们有 try… finally 在家”

发布: (2025年12月28日 GMT+8 14:42)
3 min read

Source: Hacker News

finally 的语言

许多拥有异常机制的语言¹也提供 finally 子句,因此你可以写出

try {
    // stuff
} finally {
    always();
}

快速检查表明,这种控制结构在 Java、C#、Python、JavaScript 中存在,但在 C++ 中不存在。

C++ 的说法是:“我们在家里有 try…finally。”

C++ 方法

在 C++ 中,让一段代码在控制离开块时执行的方式是把它放在析构函数里,因为析构函数会在控制离开块时运行。这正是 Windows Implementation Library 的 wil::scope_exit 函数所使用的技巧:你提供的 lambda 被放入一个自定义对象中,该对象的析构函数会执行该 lambda。

auto ensure_cleanup = wil::scope_exit([&] { always(); });

/* stuff */

虽然原理相同,但每种语言在 finally 或析构函数本身抛出异常的情况下都有一些细微差别。

异常处理语义

如果控制在没有异常的情况下离开受保护块,那么在 finally 块或析构函数中出现的任何未捕获异常都会从 try 块抛出。所有语言似乎都对此达成一致。

如果控制在带有异常的情况下离开受保护块,并且 finally 块或析构函数抛出异常,行为则因语言而异。

语言两者都抛出异常时的行为
Java、C#、JavaScriptfinally 块抛出的异常会覆盖原始异常;原始异常会丢失。
Python (≥ 3.2)原始异常被保存为新异常的 context,但传播的是新异常。
C++如果析构函数抛出异常且该析构函数是因另一个异常而运行,则会触发自动程序终止。¹

因此,C++ 为你提供了在作用域离开时运行代码的能力,但你的代码最好不要让异常逃逸,否则后果自负。

注释

¹ Microsoft 编译器还支持用于结构化异常处理的 __try__finally 关键字。这些关键字旨在用于 C 代码;在 C++ 中使用它们可能会以令人困惑的方式与 C++ 异常交互。参见 Old New Thing 的讨论。

² 这就是 wil::scope_exit 文档中说明如果 lambda 抛出异常会终止进程的原因。另一函数 wil::scope_exit_log 会记录并随后忽略 lambda 抛出的异常。没有提供类似 Java 行为的变体。

Back to Blog

相关文章

阅读更多 »

C++ 说 “我们在家尝试”。

许多具有异常机制的语言¹也都有 finally 子句,所以你可以这样写: cpp try { // ⟦ stuff ⟧ } finally { always; } 快速检查表明,这种 co…

C++ says “我们在家尝试”

在其他语言中的 finally 许多拥有异常机制的语言¹也都有 finally 子句,因此你可以这样写: cpp try { // stuff } finally { always; } 一个快速的 c…