我应该从 std: : 异常继承吗?

我看到至少有一个可靠的来源(我使用的一个 C + + 类)建议 C + + 中特定于应用程序的异常类应该从 std::exception继承。我不清楚这种方法的好处。

在 C # 中,从 ApplicationException继承的原因很清楚: 您获得了一些有用的方法、属性和构造函数,只需要添加或覆盖您需要的内容。对于 std::exception,似乎只有一个要重写的 what()方法,您可以自己创建这个方法。

那么,使用 std::exception作为应用程序特定异常类的基类(如果有的话)有什么好处呢?有什么好的理由不从 std::exception继承吗?

36260 次浏览

The reason why you might want to inherit from std::exception is because it allows you to throw an exception that is caught according to that class, ie:

class myException : public std::exception { ... };
try {
...
throw myException();
}
catch (std::exception &theException) {
...
}

Difference: std::runtime_error vs std::exception()

Whether you should inherit from it or not is up to you. Standard std::exception and its standard descendants propose one possible exception hierarchy structure (division into logic_error subhierarchy and runtime_error subhierarchy) and one possible exception object interface. If you like it - use it. If for some reason you need something different - define your own exception framework.

The main benefit is that code using your classes doesn't have to know exact type of what you throw at it, but can just catch the std::exception.

Edit: as Martin and others noted, you actually want to derive from one of the sub-classes of std::exception declared in <stdexcept> header.

If all your possible exceptions derive from std::exception, your catch block can simply catch(std::exception & e) and be assured of capturing everything.

Once you've captured the exception, you can use that what method to get more information. C++ doesn't support duck-typing, so another class with a what method would require a different catch and different code to use it.

Reason for inheriting from std::exception is it "standard" base class for exceptions, so it is natural for other people on a team, for example, to expect that and catch base std::exception.

If you are looking for convenience, you can inherit from std::runtime_error that provides std::string constructor.

Since the language already throws std::exception, you need to catch it anyway to provide decent error reporting. You may as well use that same catch for all unexpected exceptions of your own. Also, almost any library that throws exceptions would derive them from std::exception.

In other words, its either

catch (...) {cout << "Unknown exception"; }

or

catch (const std::exception &e) { cout << "unexpected exception " << e.what();}

And the second option is definitely better.

There is one problem with inheritance that you should know about is object slicing. When you write throw e; a throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw. That could be not what you're expecting. Example of problem you could find here.

It is not an argument against inheritance, it is just 'must know' info.

You should inherit from boost::exception. It provides a lot more features and well-understood ways to carry additional data... of course, if you're not using Boost, then ignore this suggestion.

The problem with std::exception is that there is no constructor (in the standard compliant versions) that accepts a message.

As a result I prefer to derive from std::runtime_error. This is derived from std::exception but its constructors allow you to pass a C-String or a std::string to the constructor that will be returned (as a char const*) when what() is called.

I once participated in the clean up of a large codebase where the previous authors had thrown ints, HRESULTS, std::string, char*, random classes... different stuff everywhere; just name a type and it was probably thrown somewhere. And no common base class at all. Believe me, things were much tidier once we got to the point that all the thrown types had a common base we could catch and know nothing was going to get past. So please do yourself (and those who'll have to maintain your code in future) a favor and do it that way from the start.

Yes you should derive from std::exception.

Others have answered that std::exception has the problem that you can't pass a text message to it, however it is generally not a good idea to attempt to format a user message at the point of the throw. Instead, use the exception object to transport all relevant information to the catch site which can then format a user-friendly message.

Whether to derive from any standard exception type or not is the first question. Doing so enables a single exception handler for all standard library exceptions and your own, but it also encourages such catch-them-all handlers. The problem is that one should only catch exceptions one knows how to handle. In main(), for example, catching all std::exceptions is likely a good thing if the what() string will be logged as a last resort before exiting. Elsewhere, however, it's unlikely to be a good idea.

Once you've decided whether to derive from a standard exception type or not, then the question is which should be the base. If your application doesn't need i18n, you might think that formatting a message at the call site is every bit as good as saving information and generating the message at the call site. The problem is that the formatted message may not be needed. Better to use a lazy message generation scheme -- perhaps with preallocated memory. Then, if the message is needed, it will be generated on access (and, possibly, cached in the exception object). Thus, if the message is generated when thrown, then a std::exception derivate, like std::runtime_error is needed as the base class. If the message is generated lazily, then std::exception is the appropriate base.

Another reason to sub-class exceptions is a better design aspect when working on large encapsulated systems. You can reuse it for things such as validation messages, user queries, fatal controller errors and so on. Rather than rewriting or rehooking all of your validation like messages you can simply "catch" it on the main source file, but throw the error anywhere in your entire set of classes.

e.g. A fatal exception will terminate the program, a validation error will only clear the stack and a user query will ask the end-user a question.

Doing it this way also means you can reuse the same classes but on different interfaces. e.g. A windows application can use message box, a web service will show html and reporting system will log it and so on.

Though this question is rather old and has already been answered plenty, I just want to add a note on how to do proper exception handling in C++11, since I am continually missing this in discussions about exceptions:

Use std::nested_exception and std::throw_with_nested

It is described on StackOverflow here and here, how you can get a backtrace on your exceptions inside your code without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions.

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHub, where a backtrace would look something like this:

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"

You don't even need to subclass std::runtime_error in order to get plenty of information when an exception is thrown.

The only benefit I see in subclassing (instead of just using std::runtime_error) is that your exception handler can catch your custom exception and do something special. For example:

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;
}