应该从std :: exception派生/继承吗?


15

在设计我的第一个“严肃的” C ++库时,我在问自己:

从中衍生出某些例外std::exception是后代吗?

即使阅读后

我还不确定。因为,除了常见的(但可能不是很好)的做法之外,我将以库用户的身份假设,std::exception仅当标准库函数在库实现中失败时,库函数才会抛出s,而它对此无能为力。但仍然,在编写应用程序代码时,对我来说非常方便,并且恕我直言,好看的只是抛出一个std::runtime_error。另外,我的用户还可以依赖定义的最小接口,例如what()或代码。

例如,我的用户提供了错误的参数,比抛出a更方便std::invalid_argument吗?因此,与在其他代码中看到的std :: exception的常用用法结合在一起:为什么不走得更远,从您的自定义异常类(例如lib_foo_exception)以及std::exception

有什么想法吗?


我不确定是否要遵循。仅仅因为您从中继承std::exception并不意味着您抛出std::exception。此外,std::runtime_error确实会从继承std::exception,而what()方法来自std::exception,而不是std::runtime_error。而且,您绝对应该创建自己的异常类,而不是抛出诸如之类的通用异常std::runtime_error
Vincent Savard 2015年

3
区别在于,当我的lib_foo_exception类从派生时std::exception,库用户将lib_foo_exception仅捕获catch ,而仅捕获库文件的库用户std::exception。所以我也可以问我的库异常根类是否应该继承自std :: exception
Superlokkus

3
@LightnessRacesinOrbit我的意思是“ ...之外”,例如“有多少种捕捉方式lib_foo_exception?” 通过继承,std::exception您可以通过catch(std::exception)OR来实现catch(lib_foo_exception)。无需派生于std::exception只有当,由catch(lib_foo_exception)
Superlokkus

2
@Superlokkus:我们有点无视catch(...)。之所以在这里是因为该语言允许您考虑的情况(以及“行为异常”的库),但这不是现代的最佳实践。
与莫妮卡(Monica)进行的轻度比赛

1
C ++中的许多异常处理设计都倾向于鼓励使用更粗略,更通用的catch站点,并且同样会采用对用户端操作进行建模的更粗略的事务。如果将其与不提倡广义捕获的语言进行比较std::exception&,例如,它们经常具有更多带有中间try/catch块的代码,这些中间块涉及非常特定的错误,这会在开始处理异常处理时降低其通用性更加强调手动错误处理,以及所有可能发生的不同错误。

Answers:


29

所有异常都应继承自std::exception

例如,假设我需要调用ComplexOperationThatCouldFailABunchOfWays(),并且想要处理它可能引发的任何异常。如果一切都继承自std::exception,这很容易。我只需要一个catch块,并且具有what()用于获取详细信息的标准接口()。

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
}

如果异常不继承自std::exception,则会变得更加难看:

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
} catch (Exception& e) {
    cerr << e.Message << endl;
} catch (framework_exception& e) {
    cerr << e.Details() << endl;
}

关于抛出runtime_error还是要invalid_argument创建自己的std::exception子类来抛出:我的经验法则是,当我需要以不同于其他错误的方式处理特定类型的错误时(即,每当我需要一个单独的catch块时)引入一个新的子类。

  • 如果我为每种可能的错误类型引入了一个新的异常子类,即使我不需要分别处理它们,那也会增加很多类的扩散。
  • 如果我重用现有的子类来表示特定的含义(即,如果此处runtime_error抛出的含义不同于一般的运行时错误),那么我将冒与现有子类的其他用法冲突的风险。
  • 如果我不需要专门处理错误,并且抛出的错误与现有标准库的错误之一(例如invalid_argument)完全匹配,则可以重用现有类。在这种情况下,添加一个新的类并没有太大的好处。(C ++核心准则在这里与我不同意-他们建议始终使用自己的类。)

C ++核心准则有进一步的讨论和例子。


所有那些“&”号!C ++很奇怪。
SuperJedi224 2015年

2
@ SuperJedi224以及所有这些不同的字母!英语很奇怪。
johannes

这个理由对我来说毫无意义。这不是catch (...)(用文字省略号)是做什么的吗?
Maxpm

1
@Maxpm catch (...)仅在您不需要对抛出的内容进行任何处理时才有用。如果您想做某事-例如显示或记录特定的错误消息,如我的示例-那么您需要知道它是什么。
乔什·凯利

9

我假设作为库用户,仅当标准库函数在库实现中失败时,库函数才会抛出std :: exceptions,而它对此无能为力

那是一个错误的假设。

提供了标准异常类型以供“普通用户”使用。它们并非旨在由标准库使用。

是的,使所有内容最终都继承自std::exception。通常,这涉及从std::runtime_error或继承std::logic_error。无论哪种类型都适合您要实现的异常。

这当然是主观的-几个流行的库完全忽略了标准异常类型,大概是将库与标准库解耦了。我个人认为这是非常自私的!这使得捕获异常变得更加困难。

就个人而言,我经常只是抛出一个std::runtime_error并完成它。但这正成为关于如何使异常类粒度化的讨论,这不是您要的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.