I've seen at least one reliable source (a C++ class I took) recommend that application-specific exception classes in C++ should inherit from std::exception
. I'm not clear on the benefits of 开发者_如何学Cthis approach.
In C# the reasons for inheriting from ApplicationException
are clear: you get a handful of useful methods, properties and constructors and just have to add or override what you need. With std::exception
it seems that all you get is a what()
method to override, which you could just as well create yourself.
So what are the benefits, if any, of using std::exception
as a base class for my application-specific exception class? Are there any good reasons not to inherit from std::exception
?
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.
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.
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.
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.
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.
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.
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) {
...
}
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.
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.
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.
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.
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.
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;
}
精彩评论