【问题标题】:Usage of try/catch blocks in C++C++ 中 try/catch 块的使用
【发布时间】:2010-10-31 09:14:06
【问题描述】:

一般来说,我倾向于将 try/catch 用于具有多个故障点的代码,这些故障点有一个共同的处理程序。

根据我的经验,这通常是在执行某些操作之前限定输入或上下文的代码,或者在执行某些操作之后限定输出。

我从文献和同事那里得到了建议,以尽量减少这些块中的代码,我接受这通常是好的建议。

我想进一步了解上述建议的基础:

  • 开销的性质是什么?
  • 最近是否有针对 try/catch 块的推荐使用(或避免使用)的开发指南?
  • 更快的处理器和更现代的编译器在多大程度上缓解了 try/catch 的问题?

提前感谢您的帮助,

AJ

【问题讨论】:

  • 我不确定我是否理解这个问题。您是否担心 try/catch 块的性能?或者询问使用 try/catch 来处理输入验证还是什么?

标签: c++ exception-handling try-catch


【解决方案1】:

我找到了a technical report on C++ performance(pdf 警告),其中包含有关异常的部分。你可能会觉得很有趣。我有同事认为 try/catch 块中的每条指令都有开销,但这份技术报告似乎并不支持这个想法。

【讨论】:

  • @ShawnChin:确实如此。我找到了一个新链接,并且还意识到我错误地分配了这篇论文。我已经更正了帖子。
  • 该报告的更新版本可在此处获得:open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf
【解决方案2】:

在 C++ 中,您不应该使用 try/catch 块来执行清理。相反,您可能希望使用模板来执行资源获取。

auto_ptr 是一个 [坏] 示例

同步锁,您将互斥锁存储为状态变量,并使用局部变量(模板或常规类)来执行 .acquire()/.release() 方法。

您做的越多,您就越不必担心在异常情况下手动释放对象。 C++ 编译器会为您完成。

【讨论】:

    【解决方案3】:

    在 c++ 中,成本取决于实现。一般来说,实现异常有两种方式:

    第一个是“表格”方法。编译器构建一组表来查找,在抛出异常的点,去哪里。当抛出异常时,它必须在调用堆栈中向上搜索每个表,直到找到可以捕获此异常的内容。由于这都是基于运行时的,因此进入或退出 try catch 不会产生任何惩罚(好),但抛出异常可能涉及许多查找,从而产生慢得多的抛出。我个人更喜欢不为 try catch 块付费,因为异常应该是非常罕见的情况。如果它们必须存储表,这也会使可执行文件变得更大。

    秒是“代码”方法。每次代码进入 try catch 块时,从概念上讲,块的位置被压入堆栈。这会在进入和退出 try-catch 块期间产生成本,但是,当抛出异常时,运行时机制可以快速弹出堆栈以查找去向。所以,抛出异常(很多?)更快,但现在进入一个块是有代价的。将 try catch 块放在紧密的低级循环中可能会产生大量开销。

    您必须检查您的特定编译器以查看他们使用的是哪一个。

    【讨论】:

      【解决方案4】:

      我发现 C++ 常见问题解答网站和相应的书对这个问题有启发性的讨论。

      http://www.parashift.com/c++-faq-lite/exceptions.html

      【讨论】:

        【解决方案5】:

        取决于编译器。你为什么不写一个带有 try-catch 块的简单函数和一个没有它的类似函数,然后比较生成的机器码?

        【讨论】:

          【解决方案6】:

          在大多数语言中,通过常规方法进入和退出 try/catch 块是免费的,只有在抛出异常时,异常处理程序才会查找处理异常的位置。

          【讨论】:

            【解决方案7】:

            关于你的第二个问题:一般准则是here, Herb Sutter 也给了很好的建议here

            【讨论】:

              【解决方案8】:

              根据我的经验,try/catch 块的最大问题是我们经常尝试过于笼统地捕获异常。例如,如果我用一个捕获 (...) 的 try/catch 块包装我的 main 函数,我基本上是在尝试不允许我的程序因抛出的异常而崩溃 b/c。

              在我看来,这种方法的问题有两个。 1)当我在测试和调试时,我没有看到任何错误,也没有机会修复它们。 2)这真的是一种偷懒的方式。而不是考虑可能出现的问题并弄清楚边缘情况是什么,我只是尽量不失败。努力不失败与努力成功有很大不同。

              【讨论】:

              • 您可以随时接住并重新投掷。注意如果异常逃脱 main() 它是实现定义的堆栈是否展开(因此可能不会调用析构函数)。因此我总是 catch(...) 并登录 main 并重新抛出,以便我得到 windows 错误消息。
              猜你喜欢
              • 2011-03-22
              • 2012-10-16
              • 2019-12-04
              • 1970-01-01
              • 1970-01-01
              • 2015-01-07
              • 2012-05-18
              • 2010-12-15
              相关资源
              最近更新 更多