对标题问题的简短回答 - 表示函数可以抛出的习语不是记录它“这个函数不会抛出”。也就是说,默认情况下,一切都可以抛出。
C++ 不是 Java,也没有经过编译器检查的异常。C++ 中没有任何内容可以让编译器告诉您您的代码声称它不会抛出,但会调用可能会抛出的东西。所以你不能完全避免这是一个运行时问题。静态分析工具可能会有所帮助,但不确定。
如果您只关心 MSVC,您可以考虑使用空异常规范或__declspec(nothrow)
不抛出的函数以及抛出throw(...)
的函数。这不会导致代码效率低下,因为 MSVC 不会发出代码来检查声明为 nothrow 的函数实际上没有抛出。GCC 可以做同样的事情-fno-enforce-eh-specs
,检查你的编译器文档。然后一切都会自动记录下来。
选项 2,应用程序范围的 try-catch 并不是真正“为了安全”,这只是因为您认为您可以对异常做一些更有用的事情(比如打印出一些东西并干净地退出),而不仅仅是让 C++ 运行时调用terminate
。如果您在编写代码时假设某些东西不会抛出,并且它实际上会抛出,那么您可能在其中任何一个实际发生之前就已经未定义,例如,如果析构函数对一致状态做出错误假设。
我通常会做 (1) 的变体:为每个函数记录它提供的异常保证 - nothrow、strong、weak 或 none。最后一个是bug。第一个是珍贵的,但很少见,只有交换函数和析构函数才需要良好的编码。是的,它会受到用户错误的影响,但是任何带有异常的 C++ 编码方式都会受到用户错误的影响。然后最重要的是,如果它可以帮助您执行(1),也可以执行(2)和/或(3)。
Symbian 有一个标准的 C++ 方言,具有一种称为“离开”的机制,在某些方面类似于异常。Symbian 中的约定是,任何可能离开的函数都必须以 L 结尾:CreateL
、ConnectL
等。平均而言,这减少了用户错误,因为您可以更容易地看到您是否正在调用可能离开的东西。正如您所料,讨厌它的人讨厌匈牙利符号的应用程序,如果几乎所有功能都离开它,它就不再有用了。正如您所料,如果您确实编写了一个名称中没有 L 的函数,那么在您找出问题之前,它可能会在调试器中很长时间,因为您的假设会使您远离实际的错误。