我在使用 C++ 代码时遇到过问题,调用者意外地抛出异常。读取您正在使用的模块的每一行来查看它是否抛出异常以及如果抛出异常,则并不总是可行或不切实际的。
是否存在处理此问题的既定惯用语或“最佳实践”?
我想到了以下几点:
-
在我们的 doxygen 文档中,我们可以在每个预期抛出异常的函数及其类型中添加注释。
-
我们可以有一个应用程序范围try/catch(...)
为了安全。
- 优点:我们不会再有任何未捕获的异常。
- 缺点:异常是在距离抛出很远的地方捕获的。很难弄清楚该做什么或出了什么问题。
-
使用异常规范
- 优点:这是处理这个问题的语言认可的方法。
- 缺点:需要重构问题库才能有效。在编译时不强制执行,因此违规会变成运行时问题,这正是我试图避免的!
这些方法有什么经验,或者我不知道的任何其他方法?
标题问题的简短回答 - 表示函数可以抛出的惯用语是not记录它“这个函数不会抛出”。也就是说,默认情况下一切都可以抛出。
C++ 不是 Java,并且没有编译器检查的异常。 C++ 中没有任何东西可以让编译器告诉你你的代码声称它不会抛出,但调用了可能会抛出的东西。所以你不能完全避免这是一个运行时问题。静态分析工具可能会有所帮助,但不确定。
如果您只关心 MSVC,您可以考虑使用空异常规范或__declspec(nothrow)
在不抛出异常的函数上,以及throw(...)
在功能上。这不会导致代码效率低下,因为 MSVC 不会发出代码来检查声明为 nothrow 的函数实际上不会抛出。 GCC 也可以做同样的事情-fno-enforce-eh-specs
,检查你的编译器文档。然后一切都将被自动记录。
选项 2,应用程序范围的 try-catch 并不是真正的“为了安全”,这只是因为你认为你可以用异常做一些更有用的事情(比如打印出一些东西并干净地退出),而不仅仅是让 C++ 运行时调用terminate
。如果您在编写代码时假设某件事不会抛出,而实际上确实发生了,那么您可能在其中任何一个实际发生之前就已经未定义,例如,如果析构函数对一致状态做出了错误的假设。
我通常会做(1)的变体:对于每个函数记录它提供的异常保证 - 不抛出、强、弱或无。最后一个是一个错误。第一个很有价值但很少见,并且只有交换函数和析构函数才严格需要良好的编码。是的,它可能会出现用户错误,但任何带有异常的 C++ 编码方式都会出现用户错误。然后,如果它可以帮助您执行(1),那么还可以执行(2)和/或(3)。
Symbian 有一个预标准的 C++ 方言,有一个称为“leave”的机制,在某些方面类似于异常。 Symbian 中的约定是任何可能离开的函数都必须以 L 结尾命名:CreateL
, ConnectL
平均而言,这会减少用户错误,因为您可以更轻松地看到您是否正在调用可能会离开的内容。正如您所料,讨厌它的人就像讨厌应用程序匈牙利表示法一样,如果几乎所有功能都离开它,它就不再有用。正如您所期望的,如果您确实编写了一个名称中没有 L 的函数,那么在您找出问题之前,它可能会在调试器中等待很长时间,因为您的假设使您远离了实际的错误。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)