何时以及如何使用异常处理?

Ume*_* MS 74 c++ windows exception-handling exception

我正在读关于异常处理.我得到了一些关于异常处理的信息,但我有几个问题:

  1. 什么时候抛出异常?
  2. 我们可以使用返回值来表示错误,而不是抛出异常吗?
  3. 如果我通过try-catch块保护我的所有功能,它不会降低性能吗?
  4. 何时使用异常处理?
  5. 我看到一个项目,该项目中的每个函数都包含一个try-catch块(即整个函数内的代码被try-catch块包围).这是一个好习惯吗?
  6. try-catch和__try __except有什么区别?

Sep*_*ram 81

这里有关于异常的全面指南,我认为是必读的:

异常和错误处理 - C++ FAQC++ FAQ lite

作为一般经验法则,当程序可以识别阻止执行的外部问题时抛出异常.如果从服务器接收数据并且该数据无效,则抛出异常.磁盘空间不足?抛出一个例外.宇宙射线阻止你查询数据库?抛出一个例外.但是如果你从你自己的程序中得到一些无效数据 - 不要抛出异常.如果您的问题来自您自己的错误代码,最好使用ASSERT来防范它.需要进行异常处理以识别程序无法处理的问题并告诉用户有关用户的问题,因为用户可以处理它们.但是你的程序中的错误并不是用户可以处理的东西,因此程序崩溃将比"answer_to_life_and_universe_and_everything的值不是42!这应该永远不会发生!!!! 11"异常少得多.

抓住一个例外,你可以用它做一些有用的事情,比如显示一个消息框.我喜欢在一个以某种方式处理用户输入的函数内部捕获异常.例如,用户按下按钮"Annihilate all hunams",在annihilateAllHunamsClicked()函数内部有一个try ... catch块说"我不能".即使摧毁hunamkind是一项复杂的操作,需要调用几十个和几十个函数,但只有一个尝试... catch,因为对于用户来说这是一个原子操作 - 一键式点击.每个函数中的异常检查都是冗余和丑陋的.

另外,我不能建议熟悉RAII - 也就是说,确保所有初始化的数据都被自动销毁.这可以通过在堆栈上尽可能多地初始化来实现,并且当您需要在堆上初始化某些东西时,使用某种智能指针.当抛出异常时,堆栈上初始化的所有内容都将被自动销毁.如果你使用C风格的哑指针,当抛出异常时你会冒内存泄漏的风险,因为没有人在异常时清理它们(当然,你可以使用C风格的指针作为你的类的成员,但要确保它们是在析构函数中处理).

  • 感谢您的宝贵意见。>“需要异常处理来识别程序无法处理的问题并告诉他们有关用户的信息,因为用户可以>处理它们”我可以返回错误值,而不是抛出异常,并且在调用函数中我可以检查错误并显示一条消息来帮助用户处理它。 (2认同)
  • @Septagram"如果您的问题来自您自己的错误代码,最好使用ASSERT来防范它.":断言不会让您继续执行不依赖于错误分支的任务(仅因为您的程序有一个函数中的错误并不意味着它不能做其他有用的事情).它不会让您使用自定义记录器.它将通过使用RAII绕过你受益的析构函数(希望在程序终止之前刷新文件缓冲区?最好不要跳过那些带有`fclose`的析构函数!).它不会给你一个完整的堆栈跟踪. (2认同)

Ytt*_*ill 12

例外在各种情况下都很有用.

首先,有一些函数,其中计算前置条件的成本如此之高,如果发现不满足前置条件,则最好只进行计算并中止异常.例如,你不能反转奇异矩阵,但是要计算它是否是单数你计算非常昂贵的行列式:它可能必须在函数内部完成,所以只需"试一试"反转矩阵并报告如果你不能通过抛出异常就会出错.这基本上是作为负前置条件使用的例外.

然后还有其他情况,您的代码已经很复杂,并且很难在调用链中传递错误信息.这部分是因为C和C++已经破坏了数据结构模型:还有其他更好的方法,但C++不支持它们(例如在Haskell中使用monad).这种用法基本上是我不能纠正这样做,所以我会抛出一个例外:它不是正确的方法,但它是实用的.

然后主要使用例外:报告何时外部前置条件或不变量(如内存或磁盘空间等资源)不可用.在这种情况下,您通常会终止程序或程序的主要部分,例外是传输有关问题的信息的好方法.C++异常是为报告错误而设计的,这些错误会阻止程序继续运行.

已知大多数现代语言(包括C++)中使用的异常处理模型已破坏.它太强大了.理论家现在已经开发出比完全开放的"扔任何东西"和"可能也许不会抓住它"模型更好的模型.此外,使用类型信息对异常进行分类并不是一个好主意.

因此,你可以做的最好的事情就是谨慎地抛出异常,当出现实际错误时,以及没有其他方法可以处理它捕获尽可能接近抛出点的异常.

  • 你能为"异常处理已被破坏"和"理论家现在开发出更好的模型"添加一些链接/参考吗? (13认同)

Jam*_*den 5

如果您的问题来自于您自己的错误代码,那么最好使用 ASSERT 来防范它。需要异常处理来识别程序无法处理的问题并告诉他们有关用户的信息,因为用户可以处理它们。但是程序中的错误不是用户可以处理的,因此程序崩溃并不能说明什么

我不同意已接受答案的这方面。断言并不比抛出异常更好。如果异常仅适用于运行时错误(或“外部问题”),那么它的std::logic_error用途是什么?

根据定义,逻辑错误几乎是一种阻止程序继续运行的条件。如果程序是一个逻辑构造,并且条件发生在该逻辑域之外,那么它如何继续?尽可能收集输入,并抛出异常!

这并不是说没有现有技术。 std::vector,仅举一例,抛出逻辑错误异常,即std::out_of_range。如果您使用标准库并且没有顶级处理程序来捕获标准异常——如果只是调用what () 和exit (3)——那么您的程序就会突然静默、终止。

断言宏是一个较弱的保护。没有恢复。除非您没有运行调试版本,在这种情况下没有执行。断言宏属于一个计算速度比今天慢 6 个数量级的时代。如果您不厌其烦地测试逻辑错误,但在生产中不使用该测试,那么您最好对自己的代码充满信心!

标准库提供了逻辑错误异常并使用它们。它们的存在是有原因的:因为逻辑错误会发生,而且是例外。当异常可以更好地处理工作时,仅仅因为 C 具有断言功能,就没有理由依赖这种原始的(并且可以说是无用的)机制。