有一个普遍接受的习惯用于指示C++代码可以抛出异常吗?

Dav*_*fal 10 c++ exception exception-specification

我在使用C++代码时遇到了问题,这些代码意外地对调用者抛出异常.读取用于查看是否抛出异常的模块的每一行并不总是可行或实际的,如果是,则抛出异常类型.

是否存在处理此问题的成语或"最佳实践"?

我想到了以下几点:

  1. 在我们的doxygen文档中,我们可以在每个预期会抛出异常及其类型的函数中添加注释.

    • 加号:简单.
    • 缺点:受用户错误的影响.
  2. 我们可以安装应用程序try/catch(...).

    • 优点:我们不会再有任何未被捕获的例外情况了.
    • 缺点:异常远离投掷.很难弄清楚做什么或出了什么问题.
  3. 使用例外规范

    • 优点:这是处理这个问题的语言认可方式.
    • 缺点:重构问题库以使其有效.在编译时没有强制执行,因此违规会变成运行时问题,这正是我想要避免的!

有这些方法的经验,还是我不知道的任何其他方法?

Ste*_*sop 9

标题问题的简短回答 - 表示函数可以抛出的成语不是记录它"此函数不抛出".也就是说,一切都可以默认抛出.

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++编码方式都会受到用户错误的影响.然后,除此之外,还可以执行(2)和/或(3),如果它可以帮助您执行(1).

Symbian有一个预先标准的C++方言,有一种称为"离开"的机制,在某些方面就像例外.在塞班的约定是,这可能会留下任何功能必须与一个L在最后被命名为:CreateL,ConnectL,等在此平均减少了用户错误的,因为你可以看到更多的轻松无论你调用一些东西,可能会离开.正如您所料,同样的人讨厌那些讨厌应用程序匈牙利语的符号,如果几乎所有功能都离开它就不再有用了.正如您所料,如果您确实编写了一个在名称中没有L的函数,那么在您找出问题之前在调试器中可能会很长,因为您的假设使您远离实际的错误.


jal*_*alf 7

解决问题的惯用方法不是表明您的代码可以抛出异常,而是在对象中实现异常安全.该标准定义了对象应实现的几个异常保证:

  • 拒绝保证:该函数永远不会抛出异常
  • 强大的异常安全保证:如果抛出异常,对象将保持其初始状态.
  • 基本异常安全保证:如果抛出异常,则对象保持有效状态.

当然,该标准记录了每个标准库类的异常安全级别.

这确实是处理C++异常的方法.不是标记哪些代码可以或不能抛出异常,而是使用RAII来确保清理对象,并考虑在RAII对象中实现适当级别的异常安全性,这样它们就能够在没有特殊处理的情况下生存下去.抛出异常.

如果异常允许您的对象处于无效状态,则异常仅会导致问题.这绝不应该发生.您的对象应始终至少实现基本保证.(并实现一个提供适当级别的异常安全性的容器类是一个启发性的C++练习;))

至于文档,当你能够确定一个函数可以抛出哪些异常时,一定要随时记录它.但一般来说,如果没有指定其他内容,则假定函数可能抛出.空抛specfication有时用于文档时的功能从来没有抛出.如果它不存在,则假设该函数可能抛出.


小智 6

坦率地说,几乎任何C++函数都可以抛出异常.您不必过于担心记录这一点,而是通过使用RAII等惯用语来使您的代码异常安全.

  • 我喜欢异常!比检查返回值好一百万倍! (10认同)