Pointers, References and Values by Michael D. Crawford Continued...

How to Throw and Catch Exceptions

You should usually throw exceptions by value and catch them by const reference.

I will not talk about handling exceptions in detail in this article. That is best left for another day. Writing exception-safe code and using exceptions effectively deserves in-depth treatment. (Besides being a necessary skill, knowing how to write exception-safe code allows you to do all kinds of cool things that would otherwise be very difficult, like reversibly failing out of and retrying complex processes.)

Exceptional C++ cover

Exceptional C++
47 New Engineering Puzzles, Programming Problems, and Solutions (C++ in Depth Series)

by Herb Sutter

[ Buy]

A good place for you to start in understanding exception safety is Herb Sutter's Exceptional C++ (also reviewed here).

Herb is the author of the Guru of the Week, C++ brain-teasers that are posted regularly to the C++ newsgroups. In Exceptional C++, Herb repeats (with some refinement) some of the Guru of the Week problems and gives detailed discussions of the answers. In particular, a sequence of GotW issues illustate the design of an exception-safe container template.

Exception-safe templates are especially hard because you cannot know whether the template parameters might throw exceptions.

However, my original article simply omitted any mention of the choice of pointer, reference or value when it comes to exceptions and I feel I should rectify at least that much.

Throw exceptions by value, and catch them by (usually const) reference.

You need to throw exceptions by value because the exception object will be destructed when it goes out of scope (at the end of the basic block where it is caught - unless you re-throw the original exception).

If you throw a pointer to an allocated object, it will not be deleted and you will leak memory.

Commonly I see engineers throwing pointers to static C string constants, as in throw "bailed out";. This is not so bad in itself, because it does not leak memory. But in the long run it is hard to ensure that only static constants are thrown; the catch clause cannot discern the difference between a string that was compiled in and a string you allocated. Code written later may want to format the string on the fly and so will need to allocate it, and this is not what you want.

Do not try to get around this by deleting a pointer that you have caught, as you might have the opposite problem - you will crash if someone throws you the address of some static memory. It is also just too much extra error prone manual labor, to expect the recipient of an exception to delete it

You should catch an exception by reference to avoid the slicing problem we have discussed previously. This is important, for example, when catching std::exception and its subclasses such as the ones declared in <stdexcept>. If you catch std::exception by value and query its what() method for a diagnostic message, you will likely get an unhelpful result like the single word "exception". Had you caught it by reference you will get the intended error message. It is helpful to subclass your own exception classes from std::exception and override what() to provide custom diagnostics.

You should generally catch by const reference, as this aids compiler optimization, unless you will be calling methods on the exception that are not declared const.

Catching by reference also avoids some overhead. Catching by value will not catch the exception object itself, but a copy.

One might consider throwing an exception by reference. You would never throw a reference to an allocated object as you will leak. But you could consider throwing a reference to a static object. You may not think you can throw a reference to a local object but you can.

The exception object that is passed to the catch clause is a copy of the object that was thrown. This is meant to avoid the problem that would be caused by the thrown object being destructed is it goes out of scope because of the exception that is throwing it. Throwing a reference will still create a copy. The only way to avoid this copy is to throw a pointer, but then you are faced with the issue of whether and how to delete it.

next button previous page contents all programming tips titles

Copyright © 2000, 2001, 2002, 2005 Michael D. Crawford. All Rights Reserved.

One Must Not Trifle With Wizards For It Makes Us Soggy And Hard To Light