我应该从std :: exceptioninheritance吗?
我已经看到至less有一个可靠的源代码(一个C ++类)推荐C ++中的特定于应用程序的exception类应该从std::exception
inheritance。 我不清楚这种方法的好处。
在C#中,从ApplicationException
inheritance的原因是明确的:你得到了一些有用的方法,属性和构造函数,只需要添加或重写你所需要的。 用std::exception
看来,你所得到的只是一个用来覆盖的what()
方法,你可以创build自己的方法。
那么,使用std::exception
作为特定于应用程序的exception类的基类有什么好处? 有没有什么好的理由不从std::exception
inheritance?
主要的好处是,使用你的类的代码不需要知道你所throw
types的确切types,但可以catch
std::exception
。
编辑:正如Martin和其他人所指出的那样,实际上你想要从<stdexcept>
头文件中声明的std::exception
的子类之一派生。
std::exception
的问题在于没有接受消息的构造函数(在标准兼容版本中)。
因此,我更喜欢从std::runtime_error
派生。 这是从std::exception
派生的,但是它的构造函数允许你传递一个C-String或者一个std::string
给构造函数,当被调用的时候会返回(作为char const*
)。
从std::exception
inheritance的原因是std::exception
的“标准”基类,所以对于一个团队中的其他人来说是自然而然的,例如期望和捕获基本的std::exception
。
如果你正在寻找方便,你可以从提供std::string
构造函数的std::runtime_error
inheritance。
我曾经参与过一个大型代码库的清理工作,在这个代码库中,前面的作者曾经抛出过整数,HRESULTS,std :: string,char *,随机类…不同的东西。 只是命名一个types,它可能被扔在某个地方。 而且根本没有共同的基础课程。 相信我,一旦我们认识到所有抛出的types都有一个共同的基础,我们可以捕获并知道什么都不会过去,那么事情就会变得更加整洁。 所以请你自己(以及将来必须维护你的代码的人)一个忙,从一开始就这样做。
你可能想要从std::exception
inheritance的原因是因为它允许你抛出一个根据这个类被捕获的exception,即:
class myException : public std::exception { ... }; try { ... throw myException(); } catch (std::exception &theException) { ... }
你应该inheritanceboost :: exception 。 它提供了更多的function和更好的理解方式来携带更多的数据……当然,如果你不使用Boost ,那么忽略这个build议。
是的,你应该从std::exception
派生。
其他人已经回答说, std::exception
有一个问题,你不能传递一个文本消息,但是通常不是一个好主意,试图格式化一个用户消息的点。 相反,使用exception对象将所有相关信息传输到catch站点,然后可以格式化用户友好的消息。
有一个遗传问题,你应该知道的是对象切片。 当你写throw e;
throw-expression初始化一个临时对象,称为exception对象,其types是通过从throw
的操作数的静态types中除去任何顶级cv限定符来确定的。 这可能不是你所期待的。 问题的例子,你可以在这里find。
这不是反对inheritance的论点,只是“必须知道”的信息。
差异:std :: runtime_error vs std :: exception()
你是否应该inheritance自己,取决于你。 标准的std::exception
和它的标准后代提出了一个可能的exception层次结构(划分为logic_error
子层次结构和runtime_error
子层次结构)和一个可能的exception对象接口。 如果你喜欢它 – 使用它。 如果由于某种原因你需要不同的东西 – 定义你自己的exception框架。
由于语言已经抛出std :: exception,所以你需要抓住它来提供体面的错误报告。 你也可以用同样的方法来处理你自己的所有意想不到的例外情况。 而且,几乎所有引发exception的库都会从std :: exception中派生出来。
换句话说,它也是
catch (...) {cout << "Unknown exception"; }
要么
catch (const std::exception &e) { cout << "unexpected exception " << e.what();}
第二个select肯定更好。
如果所有可能的exception都来自std::exception
,那么catch块可以简单地catch(std::exception & e)
并确保捕获所有的东西。
一旦你捕捉到exception,你可以用what
方法来获取更多的信息。 C ++不支持鸭子input,所以另一个有what
方法的类会需要不同的catch和不同的代码来使用它。
是否从任何标准exceptiontypes派生是第一个问题。 这样做可以为所有标准库exception和您自己的exception处理程序启用一个exception处理程序,但是它也鼓励这样的catch-them-all处理程序。 问题是,只应该捕捉一个知道如何处理的exception。 例如,在main()中,如果在退出之前what()string被logging为最后的手段,那么捕获所有的std :: exceptions可能是一件好事。 但是,在其他地方,这不太可能是一个好主意。
一旦你决定是否从一个标准的exceptiontypes派生,那么问题是应该是基础。 如果您的应用程序不需要i18n,那么您可能会认为在呼叫站点格式化消息与保存消息并在呼叫站点生成消息一样好。 问题是可能不需要格式化的消息。 最好使用懒惰的消息生成scheme – 也许预先分配内存。 然后,如果需要消息,它将在访问时生成(并可能caching在exception对象中)。 因此,如果消息在被抛出时生成,那么std :: exception派生,就像std :: runtime_error需要作为基类一样。 如果消息是懒惰生成的,那么std :: exception是适当的基础。
分类exception的另一个原因是在处理大型封装系统时更好的devise方面。 你可以重用它,如validation消息,用户查询,致命的控制器错误等。 而不是重写或重新钩住你所有的validation信息,你可以简单地把它“捕捉”在主源文件上,而是把错误抛到整个类集合中的任何地方。
例如,一个致命的exception会终止程序,一个validation错误只会清除堆栈,一个用户查询会询问最终用户一个问题。
这样做也意味着您可以重用相同的类,但在不同的接口上。 例如,一个Windows应用程序可以使用消息框,一个Web服务将显示HTML和报告系统将logging它等等。
虽然这个问题已经很老了,并且已经得到了很好的回答,但是我只是想补充一下如何在C ++ 11中进行适当的exception处理,因为我在关于exception的讨论中不断忽略这个问题:
使用std::nested_exception
和std::throw_with_nested
在StackOverflow 这里和这里描述了如何通过简单地编写一个适当的exception处理程序来重新引发嵌套的exception,从而可以在代码中回溯exception,而无需debugging器或繁琐的日志logging。
既然你可以用任何派生的exception类来做到这一点,你可以添加很多信息到这样的回溯! 您也可以在GitHub上看看我的MWE ,在这里,回溯会看起来像这样:
Library API: Exception caught in function 'api_function' Backtrace: ~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed ~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
你甚至不需要std::runtime_error
,以便在引发exception时获得大量的信息。
我在子类中看到的唯一好处(而不是仅仅使用std::runtime_error
)是您的exception处理程序可以捕获您的自定义exception并做一些特殊的事情。 例如:
try { // something that may throw } catch( const MyException & ex ) { // do something specialized with the // additional info inside MyException } catch( const std::exception & ex ) { std::cerr << ex.what() << std::endl; } catch( ... ) { std::cerr << "unknown exception!" << std::endl; }