使用哪个更好,__try/__except 块还是 try/catch 块?

32
我想知道捕获我抛出的异常哪种方式更好:是使用__try / __except块还是try / catch块?
我正在使用C++编写程序,并且该程序只在Windows上使用,因此可移植性不是问题。
谢谢!
5个回答

46

它们是两个非常不同的东西。try/catch 是您所熟悉的 C++ 关键字。而 __try/__except 则用于捕获系统异常,例如除零或访问无效等 Windows 自身引发的异常。有关此功能的详细描述,请参阅MSDN Library文章

您还可以利用 Windows SEH 功能来捕获 C++ 异常,但无法从中获取抛出的异常对象,因此在实际处理异常时上下文信息将为零,这是不可取的。最好的方法是永远不要捕获 SEH 异常,因为它们总是令人讨厌的。如果确实需要将它们结合起来,则可以使用 _set_se_translator() 将 SEH 异常转换为 C++ 异常。


19
您应该使用一个try/catch块。
正如其他人已经回答的那样,__try / __except是用于捕获SEH(Windows生成的错误),而不是用于捕获通用异常。
最重要的是,__try__catch在抛出异常时可能无法运行C++解构函数或正确解开堆栈。
除了极少数情况外,您永远不应该尝试捕获SEH异常。
编辑:好吧,我对此有信心(这就是我一直被告知的内容),但@Hans说显然有一个编译器开关可以用来更改此设置。 我认为关于/EHa的文档在这里发生了误导,或者至少是不完整的。 如果有人找到确凿的文档证明这是错误的,我会很高兴删除这个答案。
即使最后证明这是错误的,你仍然应该使用trycatch,因为它们是标准的,而__try__except则不是。

2
@David:没错。__try__except严格来说是基于C的API/ABI。 - Billy ONeal
8
不准确。使用/EHa编译可以确保在堆栈展开期间调用C++析构函数。 - Hans Passant
1
@Hans:你能找到相关的文档吗?我理解/EHa仅仅允许C++异常处理结构(当你使用trycatch时)捕捉SEH异常,但是当使用__try__except时,C++堆栈仍然无法正确地展开。 - Billy ONeal
2
/EH文档就是全部内容。是的,它并不完美。/EHa会抑制编译器优化,当编译器检测到没有任何代码可以抛出C++异常时,会省略异常清理处理程序。您可以在查看机器代码时看到它们。 - Hans Passant
1
@Alex:__except 可以捕获 C++ 异常;但我的理解是,在这样做时,它不能保证提供 C++ 栈展开语义。除非像 Hans 所说的那样,在 /EHa 下。 (请不要使用 /EHa - Billy ONeal
显示剩余8条评论

6

__try/__except 是为调用不支持异常但使用结构化错误代码/处理机制的Win32 C代码而设计的。 __try/__except 将把C错误转换为类似于C++ try/catch 的异常块。

更多信息,请参见此MSDN文章


4

标准的C++使用try/catch块,因此如果您需要基于标准C++库的“标准”异常机制,则建议使用它们。

但是,如果您计划使用Windows SDK提供的结构化异常处理(参见这里),则请使用__try/__except


-1:STL与异常处理完全无关。 - Billy ONeal
2
STL与异常处理有绝对的关系:std::exception是推荐的异常基类,即使它不是强制性的。 - Cătălin Pitiș
std::exception 不是 STL 类。 - Billy ONeal
抱歉。标准C++库的措辞更好吗? - Cătălin Pitiș
@Cătălin Pitiș:是的,那样会更好,但仍然不够苛刻 :)(如果进行编辑,我会撤销我的反对票) - Billy ONeal

2

一旦你抛出了某个东西,你就没有太多的选择来捕获它。如果你抛出C++异常(即使用throw),那么请使用try/catch。如果你抛出Windows异常(即使用RaiseException),那么请使用__try/__except。试图混合它们只会给你的生活增加不必要的麻烦。


3
但是你不应该抛出 Windows 异常,因为它们无法回滚堆栈。 - Billy ONeal
@BillyONeal 看起来这是在 Windows 10 之前设置线程名称的唯一方法:https://learn.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2019 - chacham15
在这种情况下,堆栈展开是无关紧要的,@chacham,因为你只是抛出异常来引起调试器的注意,而不是改变程序的流程。你立即捕获并丢弃异常。然而,重点在于,你没有真正选择如何捕获该异常。你使用RaiseException抛出它,所以必须使用__except来捕获它。 - Rob Kennedy
当然可以,但是“但你永远不应该抛出Windows异常”并不完全准确。 - chacham15

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接