C++ - 返回代码异常的参数

Ste*_*eve 29 c++ exception

我正在讨论在新的C++项目中采用哪种方式.由于以下原因,我赞成对返回代码的异常(仅限特殊情况) -

  1. 构造函数无法提供返回代码
  2. 将故障路径(应该非常罕见)与更清晰的逻辑代码分离
  3. 在非特殊情况下更快(不检查是否/否则数十万次)
  4. 如果有人搞砸了返回代码设置(忘记返回FAIL),可能需要很长时间才能追踪.
  5. 来自错误中包含的消息的更好信息.(有人向我指出,返回枚举可以对错误代码执行相同的操作)
  6. 从Jare​​d Par不可能忽略没有代码专门设计来处理它

这些是我从思考它和谷歌搜索中得出的要点.我必须承认,在过去的几年中,我倾向于使用C#工作的例外情况.请在退货代码上发布使用例外的进一步原因.对于那些喜欢返回代码的人,我也愿意倾听你的推理.谢谢

Nik*_*nić 47

我认为这篇文章总结了一下.

使用例外的参数

  1. 异常将错误处理代码与正常的程序流分开,从而使代码更具可读性,健壮性和可扩展性.
  2. 抛出异常是从构造函数报告错误的唯一简洁方法.
  3. 与错误代码不同,例外很难被忽略.
  4. 异常很容易从深层嵌套函数传播.
  5. 例外可以是,通常是用户定义的类型,它们携带的信息比错误代码多得多.
  6. 通过使用类型系统将异常对象与处理程序进行匹配.

反对使用异常的参数

  1. 异常通过创建多个不可见的退出点来破坏代码结构,这使得代码难以阅读和检查.
  2. 异常容易导致资源泄漏,尤其是在没有内置垃圾收集器且最终阻塞的语言中.
  3. 学习编写异常安全代码很难.
  4. 例外是昂贵的,并且违背了仅为我们使用的东西付费的承诺.
  5. 遗留代码很难引入异常.
  6. 对于执行属于正常程序流的任务,异常很容易被滥用.

  • 关于反对的论点:1:无效.抛出异常并不是不可见的.但是,允许从被调用代码传递异常可以.2. RAII.3.不要抛弃析构函数.见对#2的回应.4.捕捉异常是昂贵的.然而,投掷通常不是.5.解释一下?6.是的,应该打败引入控制逻辑等异常的开发人员. (8认同)
  • 通常,在您的SO答案中加上摘要是很友好的,这样路人就可以了解文章所说的内容. (3认同)
  • 尤其是因为您链接的页面可能会在明天被删除.如果你没有在这里总结,你的答案将毫无价值 (3认同)
  • @NathanErnst:3实际上是一个有效点,如果异常发生在中途,有很多代码会使数据处于无效状态.编写异常安全代码很难. (2认同)

Gre*_*g D 10

我听说过更喜欢返回代码优于异常的最好情况就是这样:

  1. 编写异常安全的代码很难 [在C++中].

凭借C#本身的大量近期经验,我可以理解你使用异常的愿望,但遗憾的是C++不是C#,而我们可以在C#中解决的很多事情在C++中最终都是致命的.

Google的风格指南中可以找到支持和反对的案例的总结.简而言之:

优点:

  • 例外允许更高级别的应用程序决定如何处理深层嵌套函数中的"无法发生"故障,而不会出现模糊且容易出错的错误代码簿记.
  • 大多数其他现代语言都使用例外.在C++中使用它们会使其与其他人熟悉的Python,Java和C++更加一致.
  • 一些第三方C++库使用异常,并在内部关闭它们使得与这些库集成更加困难.
  • 异常是构造函数失败的唯一方法.我们可以使用工厂函数或Init()方法对其进行模拟,但这些方法分别需要堆分配或新的"无效"状态.
  • 在测试框架中,例外非常方便.

缺点:

  • 将throw语句添加到现有函数时,必须检查其所有可传递调用者.要么他们必须至少制定基本的异常安全保证,要么他们必须永远不会捕获异常,并对结果终止的程序感到满意.例如,如果f()调用g()调用h(),并且h抛出f catch的异常,则必须小心g或者可能无法正确清理.
  • 更一般地说,异常使得程序的控制流程难以通过查看代码来评估:函数可能会返回您不期望的位置.这导致可维护性和调试困难.您可以通过一些关于如何以及在何处使用异常的规则来最小化此成本,但代价是开发人员需要了解和理解的更多内容.
  • 异常安全需要RAII和不同的编码实践.需要大量的支持机制才能使编写正确的异常安全代码变得容易.此外,为了避免要求读者理解整个调用图,异常安全代码必须将写入持久状态的逻辑隔离到"提交"阶段.这将带来好处和成本(可能是您被迫混淆代码以隔离提交的地方).允许例外将迫使我们总是支付这些费用,即使它们不值得.
  • 打开异常会为每个生成的二进制文件添加数据,从而增加编译时间(可能略微增加)并可能增加地址空间压力
  • 异常的可用性可能会鼓励开发人员在不合适时抛弃它们,或者在不安全的情况下从中恢复.例如,无效的用户输入不应导致抛出异常.我们需要使样式指南更长时间来记录这些限制!

我建议阅读并理解利弊,然后根据这些来决定你自己的项目.你没有google所拥有的相同软件,所以对他们来说有意义的可能对你没有意义(这就是为什么我省略了他们的结论).

  • 谷歌不使用它们的一个重要原因是谷歌不使用它们.:)基本上,他们被卡住了.来自相同的编码标准文档:"因为我们想在谷歌使用我们的开源项目,如果这些项目使用例外情况很难这样做,我们也需要建议不要考虑谷歌开源项目中的例外情况.如果我们不得不从头再做一遍,那可能会有所不同." (8认同)
  • 编写异常安全代码可能很难,但编写依赖于检查所有返回代码的正确代码并不容易. (3认同)
  • 你错过了Pros中的第三个子弹:C++标准库使用了很多东西的例外.关闭异常,并且(a)你不再运行标准C++,并且(b)C++库变得不可靠. (3认同)

Jar*_*Par 6

恕我直言,偏好返回代码的首要原因是你不能无声地忽略异常.这样做至少需要少量的额外代码.

  • "对于返回代码,程序可以继续正常工作" - 你会错过另一种情况,这实际上是最常见的返回值错误处理:程序继续工作,但它工作_improperly_.最好的情况是,在那种情况下,你会在以后的某个时间遇到一个奇怪的崩溃,这很难调试,因为你看到的只是一些数据不变量被破坏了,不知道是谁做了它们.最糟糕的情况是您的程序根本不会崩溃,但它无法正常工作,并且会无声地破坏数据(内存中,最终可以保存到磁盘). (15认同)
  • @tloach如果你不处理它,它不会被忽略.它会通常以崩溃的形式对您的程序造成严重破坏.忽略失败的HRESULT很容易 (3认同)

Dav*_*ley 5

对异常错误情况使用例外。您有一些很好的论据,我想提出一些反对意见。

首先,标准的C ++库本身就在各处使用异常。没有容器类或iostream,就不能使用它们。由于许多有用的功能将要使用它们,因此在没有它们的情况下尝试相处会带来很多问题。

其次,一旦学会了如何编写异常安全代码就不难了。它需要一致的RAII,但这仍然是您应该编写的方式。您应该采用构造提交方法,但这通常是一个优点,并且避免了一些细微的错误。(例如,自我分配问题通过复制交换方法完全消失了。)以我的经验,异常安全代码通常看起来更好。这是C ++程序员必须学习的东西,但是C ++程序员必须学习很多东西,而不仅仅是这些。

第三,如果您将例外限制在特殊情况下,则对性能的影响最小。而且,正如Pavel Minaev指出的那样,如果您必须将错误代码返回结果,则可能会对性能产生影响,因为C ++并未设置为轻松返回多个值。

第四,确实很难使较旧的代码具有异常安全性。但是,这是一个新项目。

因此,我没有很好的理由在特殊情况下不抛出异常,也有很多理由这样做。