【发布时间】:2011-01-20 13:29:50
【问题描述】:
对于库代码,创建并抛出自定义异常类(library::Exception)是更好的做法,还是只抛出标准异常(runtime_error、invalid_argument 等)?
【问题讨论】:
-
我不认为这些问题是重复的。一个询问异常,一个询问异常基类。
对于库代码,创建并抛出自定义异常类(library::Exception)是更好的做法,还是只抛出标准异常(runtime_error、invalid_argument 等)?
【问题讨论】:
通常您应该在stdexcept 标头或其子类中抛出类的实例。究竟什么类有意义取决于具体问题。我认为抛出“类别类”std::logic_error 和std::runtime_error 的实例很少有用,因为它们没有内在含义;它们用于区分可能发生异常的两种主要情况:
如果函数被调用,std::logic_error 的子类应该被抛出,但并非所有先决条件都得到满足。例外是调用者的错误,因为它未能提供必要的先决条件。对于此类别,您通常必须在抛出和未定义行为之间进行选择;这是健壮性和效率之间的权衡(例如std::vector::at() 与std::vector::operator[]。这些异常通常无法处理;它们是程序中的错误的结果。
std::runtime_error 的子类如果满足所有先决条件但函数不能满足后置条件或由于程序无法控制的原因破坏不变量(例如,文件不存在,网络连接丢失,或没有足够的可用内存)。通常应该处理这些异常。
我认为可用的 logic 错误类(例如invalid_argument)通常已经足够好,因为如果它们被引发,代码通常必须被修复,并且没有理由进行精心处理例行公事。另一方面,标准库中的runtime 错误类本质上不太灵活,主要涵盖标准库本身必须抛出异常的区域。对于您的应用程序,您应该几乎总是从这些运行时错误类继承。例如,如果构造函数分配资源失败,管理操作系统资源 X 的类应该抛出一个继承自 std::runtime_error 的 X_creation_error。
多重虚拟继承通常对异常类很有用。您可以从std::runtime_error 或其他stdexcept 类、从某些特定于您的库的“标记类”以及从boost::exception 继承,以获得Boost.Exception library 的额外好处。强烈推荐 Boost.Exception 教程,尤其是 Exception types as simple semantic tags 和 Using virtual inheritance in exception types。
【讨论】:
通常最好专门化(继承)一个标准异常并抛出它。
这样就可以通过捕获std::exception 将其捕获为通用异常,但如果您需要更专业的代码,您也可以专门捕获您的自定义异常类型。
另请参阅C++Faq,了解扔什么。
【讨论】:
std::invalid_argument,一般抛出logic_error或runtime_error,并为bad_my_custom_dynamic_cast之类的东西创建自定义异常。
std::logical_error 或std::invalid_argument 语义的异常,那很好:您可以使用那个。但是,如果您的语义(甚至稍微)不同,我认为最好使用新的异常类型。如果需要捕获异常的代码捕获了一个通用异常(std:: 一个),这不会有任何区别,但如果代码可能需要针对您的确切异常进行专门化,则会有很大的不同。跨度>
恕我直言自定义异常。您图书馆的客户会对此表示赞赏。他会知道究竟是什么引发了异常。
【讨论】:
根据经验,您需要的异常类很少。
在我的大部分代码中(主要是数字——如果你这样做,例如 IO,情况就不同了),我抛出标准异常(runtime_error,invalid_argument,...),因为它们往往表示某些东西无法轻易从其中恢复(可能invalid_argument 除外),并且可能不会被用户代码捕获(可能在顶层向用户抛出消息框除外)。
如果存在一些旨在被典型用户代码捕获的异常,例如。 bad_custom_cast 或 bad_market_data_identifier,而不是冒泡到 main(如数字例程中的失败,或 bad_alloc),我创建了一个自定义类。但这实际上很少见。
【讨论】: