C++,__try 和 try/catch/finally

Posted

技术标签:

【中文标题】C++,__try 和 try/catch/finally【英文标题】:C++, __try and try/catch/finally 【发布时间】:2011-10-26 08:14:28 【问题描述】:

我有点想知道 C++ 的 try/catch/finally 块。我见过这些带有两个下划线的命令,比如 __try。但是 MVSC 2010 项目也可以在没有下划线的情况下运行。那么什么时候需要这些下划线呢?

【问题讨论】:

finally 不在 C++ 中,对于正确编写的 C++ 代码来说确实毫无意义。 @bames53 如果您不通过一些解释来支持您的陈述,那么说某事毫无意义并没有多大帮助。 @Shakaron finally 毫无意义,因为 RAII 几乎在所有情况下都是一种更好的方法,而scope_guard 负责剩下的 0.1%。 @bames53。感谢RAII 的提示。这当然是一个很好的做法。有点痛苦,如果您使用的课​​程不遵循它,那么 finally 可能仍然有用。例如:Transaction * t = manager->BegintTransaction();尝试 t->Write("Foo"); catch (...) LogError();扔; 最后 manager->CloseTransaction(t); ` 这样,您无需在重新抛出异常之前关闭try 块和catch 块中的事务。 @Shakaron 在没有将类型设计为自动清理并且您必须通过清理手动对其进行改造的情况下,您可以使用类似 scope_guard 的东西。 Here's 你的例子看起来如何。 IMO 这仍然比finally 好。这还不是标准的,但 here's the proposal,您可以使用现有的 3rd-party 库。 【参考方案1】:

在 Windows 上,操作系统级别支持异常。它们被称为结构化异常处理 (SEH),大致相当于 Unix 信号。为 Windows 生成代码的编译器通常利用这一点,他们使用 SEH 基础结构来实现 C++ 异常。

根据 C++ 标准,throwcatch 关键字只抛出和捕获 C++ 异常。 MSVC 编译器对应的 SEH 异常代码为 0xe06d7363。最后 3 个字节是“msc”的 ASCII 码。

将它与操作系统支持统一还意味着在堆栈展开期间将调用 C++ 析构函数以处理 SEH 异常。执行展开的代码位于 Windows 内部,并以与任何 SEH 完全相同的方式处理由 throw 引发的 SEH。但是,Microsoft 编译器进行了优化,试图避免生成确保在所有情况下都调用析构函数所需的代码。如果它可以证明在控制对象生命周期的范围块内没有 throw 语句,那么它会跳过注册代码。这与异步 SEH 异常不兼容,如果您打算捕获 SEH 异常,则应使用 /EHa 编译选项来抑制此优化。

有很多 SEH 异常类型。操作系统可以生成的在 ntstatus.h SDK 头文件中列出。此外,您可能会与使用 SEH 实现自己的异常处理的代码进行互操作,它们将使用自己的异常代码。与 .NET 一样,托管异常使用 0xe0434f4d(“com”)异常代码。

要在 C++ 程序中捕获 SEH 异常,您必须使用非标准的 __try 关键字。 __except 关键字类似于 C++ catch 关键字。它具有更多功能,您可以指定一个异常过滤器表达式来确定是否应捕获活动异常。一切皆有可能,但您通常只查看传递的异常信息以查看您是否有兴趣处理它。 __finally 关键字允许您编写在处理异常后运行的代码。在 C++ 中没有等价物,但在其他语言中并不少见。

正如 cmets 所指出的,所有这些都没有得到很好的记录。证据就在布丁里。这是您可以使用的示例程序。它演示了 SEH 异常如何仍然允许调用 C++ 析构函数,前提是您使用 /EHa 进行编译,以及如何在 SEH 之上实现 C++ 异常。需要 MSVC 编译器,使用 Ctrl+F5 运行以避免调试器有帮助:

#include "stdafx.h"
#include <windows.h>
#include <iostream>

// NOTE: the value of the C/C++, Code Generation, Enable C++ Exceptions setting in important
// Try it both with /EHsc (the default) and /EHa to see the difference

class Example   
public:
    ~Example()  std::cout << "destructed" << std::endl; 
;

int filterException(int code, PEXCEPTION_POINTERS ex) 
    std::cout << "Filtering " << std::hex << code << std::endl;
    return EXCEPTION_EXECUTE_HANDLER;


void testProcessorFault() 
    Example e;
    int* p = 0;
    *p = 42;


void testCppException() 
    Example e;
    throw 42;


int main()

    __try 
        testProcessorFault();
    
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) 
        std::cout << "caught" << std::endl;
    
    __try 
        testCppException();
    
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) 
        std::cout << "caught" << std::endl;
    
    return 0;

输出:

Filtering c0000005
destructed
caught
Filtering e06d7363
destructed
caught

【讨论】:

一个指向文档的链接,该链接明确说明了答案第 3 段中的推理? 我不知道, __try 的行为仅记录在 C 编译器中。 /EHa 的文档中有一些内容,但还不够。如果文档一直很出色,就不会有任何 SO :) 嗯,很形而上学,我们不都只是感知人类的蝴蝶吗?这只蝴蝶认为过去 15 年来他一直在用 C++ 在 Windows 上编写机器控制软件。让 SEH 崩溃的机器在我们的客户中不是很受欢迎。不要太担心否决票,您的回答很受欢迎,您可能会因此获得徽章。另一种看法。 不要误解我的评论,我非常感谢您,自从我开始参加这里以来,您就已经看到了出色的答案。至于否决票,我不会为此而汗流浃背,但如果我或其他任何人都没有从否决票中学到任何东西,那么它对任何人都没有好处。我只是为您提供的相互矛盾的观点寻求书面证据或合乎逻辑的解释,因为我必须自己相信它才能纠正我可能有的错误理解。在任何情况下/情况下,我都没有冒犯的意思,所以请不要采取任何措施。 我参加这个聚会迟到了,但这篇文章是最后的推动,它使我们对处理 SEH 异常有了初步的了解,以一种既可以保护我们的客户免受崩溃又可以恢复的方式一些关于故障位置的有用信息。谢谢你,汉斯穿过丛林,让光线进来。【参考方案2】:

__try / __except 用于捕获 SEH (windows generated errors) 不是用于捕获一般异常。

try / catch 是 C++ 标准为处理一般 C++ 异常指定的内容。

对于您编写的标准 C++ 代码,您应该始终使用 try/ catch 而不是 __try / __except

另外,finally 不是 C++ 标准指定的构造,它适用于您,因为它是 Microsoft compiler extension

【讨论】:

try/catch 不会处理 SEH。当/EHa (Yes With SEH Exceptions) 使用时,try/catch 将同时处理:C++ 和 SEH 异常。缺点是catch(...) 会处理 SEH,但你不知道异常代码。使用EHa 时将调用析构函数。 try__try 不能在同一个函数中混合使用。因此最好有两个函数(一个调用另一个):一个处理try,另一个处理__try(没有/EHa【参考方案3】:

__try/__except is Microsoft specific 如果您希望您的代码可以与其他编译器(例如 c g++)(或)在另一个操作系统中编译,请避免使用它们,并坚持使用 standard try/catch 语句

【讨论】:

这是正确的,但您将失去捕捉 Windows 的 SEH 异常的能力。这就是你需要非标准处理的时候。

以上是关于C++,__try 和 try/catch/finally的主要内容,如果未能解决你的问题,请参考以下文章

c++异常捕获

VC++异常捕获__try...__except和try...catch的使用介绍(附源码)

JS中try....catch

__invoke,try{}catch(){},microtime(),is_callable()

Python学习笔记__8章错误调试和测试__8.1章错误处理

人生苦短_我用Python_Try_Exception异常捕捉_007