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.
Of course, the Standard C++ exceptions are a good example of an exception hierarchy and one on which you can build.
Multiple inheritance (MI)
As you’ll read in Chapter 9, the only
Catch by reference, not by value
We explained in the section "Exception matching" earlier that 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 have come to rely on an implied guarantee that object creation is always successful, which is not unreasonable in C in which 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
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 for pointers that reference heap memory.
Overhead
When an exception is thrown, there’s considerable runtime overhead (but it’s