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

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

Source: Hacker News

概述

许多带有异常机制的语言¹ 也都有 finally 子句,因此你可以写:

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

快速检查会发现,这种控制结构在 Java、C#、Python、JavaScript 中都有,但 在 C++ 中没有

C++ 的做法

C++ 提供了一种在离开块时执行代码的方法,即使用析构函数,因为析构函数会在作用域退出时运行。这正是 Windows Implementation Library 的 wil::scope_exit函数所采用的技术:你提供的 lambda 被存放在一个自定义对象中,该对象的析构函数会运行该 lambda。

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

/* ⟦ stuff ⟧ */

异常处理的细节

虽然原理相同,但各语言在处理 finally 块或析构函数本身抛出异常的情况时有所不同。

受保护块中没有异常

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

受保护块中抛出异常 finally/析构函数也抛出异常

语言当受保护块和 finally/析构函数都抛出异常时的行为
Java、Python、JavaScript、C#来自 finally 块的异常 覆盖 原始异常,导致原始异常丢失。更新: Python 3.2 现在会将原始异常保存为新异常的 context,但仍是新异常会传播。
C++如果在已有活动异常的情况下析构函数抛出异常,程序会终止。¹

因此,C++ 允许你在作用域退出时运行代码,但如果想避免程序终止,这段代码 不能让异常逸出

脚注

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

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

Back to Blog

相关文章

阅读更多 »

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

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

Go的秘密生活:错误处理

第12章:碎玻璃的声音 星期一的早晨,厚重的灰色雾气笼罩着整座城市。档案馆内部,寂静至极,随后被打破……