我们知道异常类有两个派生类: 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时(严格来说,圆圈不是椭圆!)会抛出哪一个?
最后,实际上,如果我抛出一个未正确分类的异常,这真的很重要吗?
答案 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_alloc
是std::exception
的直接子类。
总之,您不应该使用std::runtime_error
或其任何预定义的子类。
答案 3 :(得分:3)
out_of_range和range_error之间的区别可以在其父类的描述中找到:
logic_error:此类定义作为报告异常引发的对象类型 程序内部逻辑错误。这些在理论上 预防的。
runtime_error:此类定义作为报告异常引发的对象类型 只能在运行时确定的错误。
域错误专门用于数学函数。因此,您的calc_ellipse_area可以非常合理地在负值上抛出域错误(如果一个或两个参数为0,则返回0)。如果椭圆也恰好是一个圆形,我认为没有任何理由可以抱怨,只要一个矩形区域函数在一个正方形上失败。
将空指针传递给不应该接收NULL的函数我将通过无效的参数异常来处理。
C ++可以让你随心所欲。对于那些可能使用您的代码的人来说,真正有用的是您在方法签名后命名的内容,并且这些名称具有合理的描述性。默认情况下,函数可以抛出任何东西 - 承诺函数不会抛出,你必须在签名中使用空的throw()。