如何明智地决定抛出哪个标准异常?

时间:2011-10-02 04:41:40

标签: c++ exception-handling error-handling

我们知道异常类有两个派生类: logic_error runtime_error

logic_error 有四个派生类: domain_error invalid_argument length_error out_of_range

runtime_error 有三个派生类: range_error overflow_error underflow_error

虽然其中一些是不言自明的,例如 overflow_error underflow_error ,但有些不太清楚,特别是 range_error ,MSDN和cplusplus只是说“报告范围错误”,这几乎没有说什么,它是如何不同 out_of_range domain_error ???

另一个问题是当我抛出异常时,我应该选择哪一个?例如,在 reverse_string(char * s)中,当s为NULL时抛出哪个异常?在 float calc_ellipse_area(float a,float b)中,当a或b为< = 0时抛出?当a == b时(严格来说,圆圈不是椭圆!)会抛出哪一个?

最后,实际上,如果我抛出一个未正确分类的异常,这真的很重要吗?

4 个答案:

答案 0 :(得分:11)

逻辑错误是程序员错误的(理论上)结果。 运行时错误是程序员无法轻易阻止的。

编写函数时,记录其前提条件和/或假设很有用。如果这些先决条件被破坏,那就是逻辑错误。

运行时错误通常是由外部因素引起的:文件操作失败,打印机脱机,无法加载DLL。

如果路径参数格式错误,那就是逻辑错误。如果它是有效的路径字符串,但不存在,或者您没有访问权限,那就是运行时错误。

有时会变得随意。糟糕的用户输入可能是运行时错误,但验证用户输入失败更多是逻辑错误。在特定情况下情况可能并不明显。

如果2011年2月1日开始,“2011年1月1日”的完成日期是无效参数,而“2011年2月31日”超出范围。 “鱼和薯条”的完成日期是域错误。长度错误通常与缓冲区大小有关,但也可能包含太多或太少的输入数据或类似的东西。

范围错误类似于超出范围的错误,除了上下文(运行时,而不是逻辑)。例如可用的打印机数量= 0.溢出和下溢或多或少都不言自明。

最后,以您和您的同事发现有意义的方式使用它们 - 或者根本不使用它们。有些人只是为所有事情使用std :: exception。

如果要以不同方式处理不同的异常,这一点非常重要。如果您只是要显示(或记录)消息并继续,那么您使用的是什么异常并不重要。

答案 1 :(得分:7)

  

例如,在reverse_string(char * s)中,当s为NULL时抛出哪个异常?

     

在float calc_ellipse_area(float a,float b)中,当a或b为< = 0时抛出?当a == b时(严格来说,圆圈不是椭圆!)会抛出哪一个?

对于这两种情况,请使用std::invalid_argument

或者您可以定义自己的null_argument来自std::logic_error(或来自std::invalid_argument)的异常,并将其用于NULL参数。

重点是,如果标准异常类都不适用于您的情况,或者您希望更多特定异常,则定义一个派生自现有类的异常。

例如,如果您想在遇到无效索引时抛出异常,则可以使用std::out_of_range或定义更多来自index_out_of_range的更具体的std::out_of_range类。

  

如果我抛出一个未正确分类的异常,这真的很重要吗?

是的,这很重要。例如,它提高了代码的可读性。如果在遇到无效索引时抛出std::logic_error,那么它不会增加可读性,但如果抛出std::out_of_range则不会增加,那么它会大大提高可读性。如果你抛出index_out_of_range,它会增加更多,因为它更具体。

答案 2 :(得分:3)

来自标准:

  
      
  • 标准C ++库提供了用于报告C ++程序中某些错误(17.6.5.12)的类。在这些类中反映的错误模型中,错误分为两大类:逻辑错误和运行时错误。
  •   
  • 逻辑错误的显着特征是它们是由程序内部逻辑中的错误引起的。从理论上讲,它们是可以预防的。
  •   
  • 相比之下,运行时错误是由超出程序范围的事件引起的。它们不能提前预测。
  •   

但是,从runtime_error派生的所有异常类型都被错误分类 - 所有这些都很容易被预防。

对于实际的运行时错误,C ++标准库相当不一致,它有时使用返回值或内部状态(例如iostream::bad()。当它确实使用异常时,它们不是从runtime_error派生的例如,std::bad_allocstd::exception的直接子类。

总之,您不应该使用std::runtime_error或其任何预定义的子类。

答案 3 :(得分:3)

out_of_range和range_error之间的区别可以在其父类的描述中找到:

  

logic_error:此类定义作为报告异常引发的对象类型   程序内部逻辑错误。这些在理论上   预防的。

     

runtime_error:此类定义作为报告异常引发的对象类型   只能在运行时确定的错误。

域错误专门用于数学函数。因此,您的calc_ellipse_area可以非常合理地在负值上抛出域错误(如果一个或两个参数为0,则返回0)。如果椭圆也恰好是一个圆形,我认为没有任何理由可以抱怨,只要一个矩形区域函数在一个正方形上失败。

将空指针传递给不应该接收NULL的函数我将通过无效的参数异常来处理。

C ++可以让你随心所欲。对于那些可能使用您的代码的人来说,真正有用的是您在方法签名后命名的内容,并且这些名称具有合理的描述性。默认情况下,函数可以抛出任何东西 - 承诺函数不会抛出,你必须在签名中使用空的throw()。