Exceptions in C++ are a very complex topic. In addition to the standard-facilities of throwing and catching them, there are things like exception-specifiers and throwing arbitrary objects. Here are some guidelines on what you should do with exceptions and what you definitely should not do.
Do's
- Use exceptions. But only for exceptional situations. Don't use exceptions for error-conditions that are likely to occur, like misconfigurations by the end-user, etc. However, when the configuration is already supplied by the software and not influenced by the user, it's indeed an exceptional situation where you should throw a C++-exception.
- Derive all your exception-classes from a standard exception-class such as std::runtime_error or at least std::exception.
- Create your own base-classes for exceptions in your application or library so you can easily distinguish your exceptions from others'. This is especially important for libraries.
- Don't create exceptions on the heap. Throw them by value and catch them by value or const-reference.
- Make your exception's error-information accessible as atomic as possible. This helps the user of your exception to create meaningful error messages for different kinds of end-users. For example, when there's a database-error, make the database-function that was executed, the line and part of the SQL-statement and the error-message accessible separately.
- Also provide a convenience-function in your exception to create a human-readable error message.
- The more different exception-classes you provide, the easier it is to handle certain types of exceptions. On the plus side, more exception-classes does not mean more work when you don't care what kind of exception is thrown when following the guideline to have a standard base-class for your application's exceptions.
Don'ts
- Don't throw anything that does not derive from std::exception.
- Try not to depend on catch(...) because you can't identify what kind of error has occured. std::exception should be the "widest" scope you catch.
- Don't use exception-specifiers. The C++ committee has done a half-assed job when inventing those and didn't correct their mistakes in C++0x. There are several reasons why exception-specifiers are broken by design:
- If any of the called functions throws an exception that's not listed in your exception-specifier, std::terminate will be called. This may happen when modifying a function that's used by any function that uses exception-specifiers, without verifying the whole code after the change.
- When you use exception-specifiers in functions that call third-party code, it's possible that they break with an update of that library. This update could even silently happen by exchanging a dynamic library that's installed system-wide, without you doing anything.
- Execption-specifiers always cost speed. Because they are not checked at compile-time, the compiler needs to add code to check for validity of the thrown exceptions, which slows down execution time even when not caring for exceptions at this point.
- Don't throw exceptions from extern "C" functions. This causes undefined behaviour, and in this case it means a crash on most popular platforms.
- Don't throw exceptions in destructors. Since destructors usually are called implicitly when the object runs out of scope, they're easy to miss. Additionally, it's not a good idea to prevent your application from freeing up resources.
- Create your exception-classes exception-safe. For example, using std::string can potentially lead to a std::bad_alloc exception being thrown.
No comments:
Post a Comment