我见过至少一个可靠的源代码(我采用的是C ++类),建议C ++中特定于应用程序的异常类应该继承自std::exception
。我不清楚这种方法的好处。
在C#中,从ApplicationException
继承的原因很明确:您可以获得一些有用的方法,属性和构造函数,只需添加或覆盖您需要的内容即可。使用std::exception
,您所得到的只是一个what()
方法来覆盖,您也可以创建自己。
那么使用std::exception
作为特定于应用程序的异常类的基类有什么好处呢?有没有充分的理由不继承std::exception
?
答案 0 :(得分:67)
主要的好处是,使用您的类的代码不必知道您throw
的确切类型,而只能catch
std::exception
。
编辑:正如Martin和其他人所说,你实际上想要从std::exception
标题中声明的<stdexcept>
子类之一派生。
答案 1 :(得分:38)
std::exception
的问题在于没有接受消息的构造函数(在标准兼容版本中)。
因此,我更倾向于从std::runtime_error
派生。这是从std::exception
派生的,但是它的构造函数允许您将{C-String或std::string
传递给将在char const*
为what()
时返回的构造函数(作为{{1}})调用。
答案 2 :(得分:17)
从std::exception
继承的原因是异常的“标准”基类,因此对团队中的其他人来说很自然地期望并且捕获基础std::exception
。
如果您正在寻找便利,可以从提供std::runtime_error
构造函数的std::string
继承。
答案 3 :(得分:12)
我曾经参与清理一个大型代码库,以前的作者已经抛出了一些内容,HRESULTS,std :: string,char *,随机类......到处都是不同的东西;只是命名一个类型,它可能被扔到某个地方。根本没有共同的基类。相信我,一旦我们达到了所有抛出的类型都有一个共同的基础我们可以捕捉并且知道什么都不会过去的事情,事情变得更加整洁。所以请你自己(以及那些将来必须维护你的代码的人)帮忙,从一开始就这样做。
答案 4 :(得分:10)
您应该继承boost::exception。它提供了更多功能和易于理解的方式来携带其他数据......当然,如果您不使用Boost,请忽略此建议。
答案 5 :(得分:9)
您可能希望从std::exception
继承的原因是因为它允许您抛出根据该类捕获的异常,即:
class myException : public std::exception { ... };
try {
...
throw myException();
}
catch (std::exception &theException) {
...
}
答案 6 :(得分:9)
是的,您应该来自std::exception
。
其他人回答说std::exception
有一个问题,你无法向它传递短信,但是在掷出点时尝试格式化用户信息通常不是一个好主意。相反,使用异常对象将所有相关信息传输到catch站点,然后可以格式化用户友好的消息。
答案 7 :(得分:6)
继承有一个问题,你应该知道的是对象切片。当您编写throw e;
时,throw-expression初始化一个名为异常对象的临时对象,其类型是通过从throw
的操作数的静态类型中删除任何顶级cv限定符来确定的。 。那可能不是你所期待的。您可以找到问题的示例here。
这不是反对继承的论据,它只是“必须知道”的信息。
答案 8 :(得分:5)
Difference: std::runtime_error vs std::exception()
是否继承是否取决于你。标准std::exception
及其标准后代提出了一种可能的异常层次结构(划分为logic_error
子层次结构和runtime_error
子层次结构)和一种可能的异常对象接口。如果你喜欢它 - 使用它。如果由于某种原因你需要不同的东西 - 定义你自己的异常框架。
答案 9 :(得分:3)
如果所有可能的异常都来自std::exception
,那么您的catch块可以只是catch(std::exception & e)
并确保捕获所有内容。
捕获异常后,您可以使用what
方法获取更多信息。 C ++不支持duck-typing,因此使用what
方法的另一个类需要不同的catch和不同的代码才能使用它。
答案 10 :(得分:3)
由于语言已经抛出std :: exception,因此无论如何都需要捕获它以提供合适的错误报告。您也可以使用相同的catch来处理您自己的所有意外异常。此外,几乎任何抛出异常的库都会从std :: exception中派生出来。
换句话说,它的任何一个
catch (...) {cout << "Unknown exception"; }
或
catch (const std::exception &e) { cout << "unexpected exception " << e.what();}
第二种选择肯定更好。
答案 11 :(得分:3)
是否从任何标准异常类型派生是第一个问题。这样做可以为所有标准库异常和您自己的异常启用一个异常处理程序,但它也鼓励这样的catch-them-all处理程序。问题是人们应该只捕获知道如何处理的异常。例如,在main()中,如果在退出之前将what()字符串作为最后的手段记录,则捕获所有std :: exceptions可能是件好事。然而,在其他地方,它不太可能是一个好主意。
一旦您决定是否从标准异常类型派生,那么问题应该是哪个基础。如果您的应用程序不需要i18n,您可能会认为在呼叫站点格式化消息与保存信息和在呼叫站点生成消息一样好。问题是可能不需要格式化的消息。最好使用延迟消息生成方案 - 可能使用预分配的内存。然后,如果需要消息,它将在访问时生成(并且可能在异常对象中缓存)。因此,如果在抛出时生成消息,则需要std :: exception派生,如std :: runtime_error作为基类。如果消息是懒惰生成的,则std :: exception是合适的基础。
答案 12 :(得分:0)
在处理大型封装系统时,子类异常的另一个原因是更好的设计方面。您可以将其重用于验证消息,用户查询,致命控制器错误等内容。您可以简单地在主源文件中“捕获”它,而不是重写或重新删除所有验证消息,但是将错误抛出到整个类集中的任何位置。
e.g。致命异常将终止程序,验证错误将仅清除堆栈,用户查询将向最终用户询问问题。
这样做也意味着您可以在不同的接口上重用相同的类。例如Windows应用程序可以使用消息框,Web服务将显示html,报告系统将记录它等等。
答案 13 :(得分:0)
虽然这个问题相当陈旧并且已经得到了很多回答,但我只是想在C ++ 11中添加一个关于如何进行适当异常处理的说明,因为我在关于异常的讨论中不断忽略这一点:
std::nested_exception
和std::throw_with_nested
StackOverflow here和here中描述了如何在代码中获取异常的回溯,而无需调试器或繁琐的日志记录,只需简单编写一个适当的异常处理程序,它将重新抛出嵌套的异常。
由于您可以对任何派生的异常类执行此操作,因此可以向此类回溯添加大量信息! 您还可以查看我的MWE on GitHub,其中回溯看起来像这样:
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
以便在抛出异常时获取大量信息。我在子类化中看到的唯一好处(而不仅仅是使用std::runtime_error
)是您的异常处理程序可以捕获您的自定义异常并执行一些特殊操作。例如:
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;
}