Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
Do use exceptions to do the following:
Fix the problem and retry the function that caused the exception.
Patch things up and continue without retrying the function.
Do whatever you can in the current context and rethrow the same
exception to a higher context.
Do whatever you can in the current context and throw a different
exception to a higher context.
Terminate the program.
Wrap functions (especially C library functions) that use ordinary
error schemes so they produce exceptions instead.
Simplify. If your error handling scheme makes things more
complicated, it is painful and annoying to use. Exceptions can be used to make
error handling simpler and more effective.
Make your library and program safer. This is a short-term
investment (for debugging) and a long-term investment (for application
robustness).
When to use exception specifications
The exception specification is like a function prototype: it
tells the user to write exception-handling code and what exceptions to handle.
It tells the compiler the exceptions that might come out of this function so
that it can detect violations at runtime.
You can t always look at the code and anticipate which
exceptions will arise from a particular function. Sometimes, the functions it
calls produce an unexpected exception, and sometimes an old function that
didn t throw an exception is replaced with a new one that does, and you get a
call to unexpected( ). Any time you use exception specifications or
call functions that do, consider creating your own unexpected( )
function that logs a message and then either throws an exception or aborts the
program.
As we explained earlier, you should avoid using exception
specifications in template classes, since you can t anticipate what types of
exceptions the template parameter classes might throw.
Start with standard exceptions
Check out the Standard C++ library exceptions before
creating your own. If a standard exception does what you need, chances are it s
a lot easier for your user to understand and handle.
If the exception type you want isn t part of the standard
library, try to inherit one from an existing standard exception. It s nice if
your users can always write their code to expect the what( ) function
defined in the exception( ) class interface.
Nest your own exceptions
If you create exceptions for your particular class, it s a
good idea to nest the exception classes either inside your class or inside a
namespace containing your class, to provide a clear message to the reader that
this exception is only for your class. In addition, it prevents pollution of
the global namespace.
You can nest your exceptions even if you re deriving them
from C++ Standard exceptions.
Use exception hierarchies
Using exception hierarchies is a valuable way to classify the types of critical errors that might be encountered with your class or library. This
gives helpful information to users, assists them in organizing their code, and
gives them the option of ignoring all the specific types of exceptions and just
catching the base-class type. Also, any exceptions added later by inheriting
from the same base class will not force all existing code to be rewritten the
base-class handler will catch the new exception.
The Standard C++ exceptions are a good example of an
exception hierarchy. Build your exceptions on top of it if you can.
Multiple inheritance (MI)
As you ll read in Chapter 9, the only essential place
for MI is if you need to upcast an object pointer to two different base
classes that is, if you need polymorphic behavior with both of those base
classes. It turns out that exception hierarchies are useful places for multiple
inheritance because a base-class handler from any of the roots of the multiply
inherited exception class can handle the exception.
Catch by reference, not by value
As you saw in the section Exception matching, you should
catch exceptions by reference for two reasons:
To avoid making a needless copy of the exception object when it
is passed to the handler.
To avoid object slicing when catching a derived exception as a
base class object.
Although you can also throw and catch pointers, by doing so you introduce more coupling the thrower and the catcher must agree on
how the exception object is allocated and cleaned up. This is a problem because
the exception itself might have occurred from heap exhaustion. If you throw exception
objects, the exception-handling system takes care of all storage.
Throw exceptions in constructors
Because a constructor has no return value, you ve previously had two ways to report an error during construction:
Set a nonlocal flag and hope the user checks it.
Return an incompletely created object and hope the user checks
it.
This problem is serious because C programmers expect that
object creation is always successful, which is not unreasonable in C because
the types are so primitive. But continuing execution after construction fails in a C++ program is a guaranteed disaster, so constructors are one of the most
important places to throw exceptions now you have a safe, effective way to
handle constructor errors. However, you must also pay attention to pointers
inside objects and the way cleanup occurs when an exception is thrown inside a
constructor.
Don t cause exceptions in destructors
Because destructors are called in the process of throwing other exceptions, you ll never want to throw an exception in a destructor
or cause another exception to be thrown by some action you perform in the
destructor. If this happens, a new exception can be thrown before the
catch-clause for an existing exception is reached, which will cause a call to terminate( ).
If you call any functions inside a destructor that can throw
exceptions, those calls should be within a try block in the destructor,
and the destructor must handle all exceptions itself. None must escape from the
destructor.
Avoid naked pointers
See Wrapped.cpp earlier in this chapter. A naked
pointer usually means vulnerability in the constructor if resources are
allocated for that pointer. A pointer doesn t have a destructor, so those
resources aren t released if an exception is thrown in the constructor. Use auto_ptr
or other smart pointer types for
pointers that reference heap memory.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |