The first version of this article was actually written in late
1995, and appeared in The C++ Report in March 1996. At the time
it was written, the C++ Standardization committee had released
the first Committee Draft of the Standard. This froze the feature
set of the language and the libraries, but ultimately much work
remained to be done on the details. The second Committee Draft of
the C++ Standard was released to the public in December 1996. The
Final Draft was approved in November 1997, though it was May 1998
before it was released by ISO to the public (two days ago
actually).

In the first Committee Draft, Chapter 15 - Exception Handling
was 7 pages long. By the second Committee Draft it had grown to 9
pages; still one of the shortest chapters in the entire Standard.
As I noted in the first version of this article, if one were to
judge the complexity of using exceptions solely on the number of
sentences needed to specify their behavior in the language, it
would be easy to conclude that exceptions were one of the easier
aspects of C++ to master.

When I started to use exceptions, it rapidly became apparent
that exceptions are much more difficult to use effectively than
it first appears. In fact, the more I explored how exceptions can
interact with non-exception handling code, the more convinced I
became that exceptions may be the most difficult feature of C++
to use. The original version of this article discussed the
problems with using exceptions and presented 8 general guidelines
(now increased to 10) and a number of specific tactics for coping
with those problems. In the intervening two years, I feel that
the original article has held up very well. What has changed
however, is some of the terminology in use in the C++ community,
and in the C++ Standard itself.

The original 1995 Committee Draft was completely silent on the
concept of "exception safety" in the Standard Library.
A number of members of the committee felt that this was
unacceptable. Around the time of the second Committee Draft, a
couple of different proposals were put forth to correct this
situation. The Final Draft Standard therefore now has some
general requirements on how certain Standard Library functions
must cope with exceptions. While the basic principals in the
Final Draft Standard correspond very closely with a couple of the
guidelines in the original article, the Standard uses different
terminology. Given that the arrival of Standard C++ finally seems
imminent, it seemed reasonable to revise the original article to
at least include a mention or two of what the Standard itself
actually says.

Using exceptions effectively is difficult for at least 3
reasons:

i. In order to be effective, exception handling must pervade
the entire application -- it can not be localized to a function
or even a class.

ii. Exceptions are almost like asynchronous events -- they do
not respect semi-colons and can appear from the middle of an
expression.

iii. Exceptions can not be ignored or 'put off' -- if not
handled

an exception will propagate up the call stack, eventually
aborting the entire program.

It is the first of these that is the real problem. When using
other features of C++ such as classes, overloaded operators,
virtual functions, or templates, it may be difficult to write the
code correctly in the first place, but once written it is easy to
use from then on. Exceptions go against this trend. Throwing an
exception is easy; writing the code that uses the function that
throws the exception is the hard part. Every function that can
have an exception propagate out of it must be designed to
propagate that exception correctly. As Tom Cargill says in [1]
"The really hard part of using exceptions is to write all
the intervening code in such a way that an arbitrary exception
can propagate from its throw site to its handler, arriving safely
and without damaging other parts of the program along the
way."

Of course, it is (ii) and (iii) that make writing all that
intervening code difficult. Consider the apparent asynchronous
nature of exceptions (exceptions are not truly asynchronous
events like a network driver interrupt, but from the outside,
they can have almost the same effect). The following statement:

while (*i++ = *j++) ;

is a typical C idiom that has been carried over (with some
major extensions) into C++. In fact, one can almost say that the
Standard Template Library was designed around such idioms. In
C++, i and j can be two different, user-defined
iterator types. For such types, both the post-increment operator
(++), and the de-reference operator (*) represent user defined
function calls. Assume that i iterates over a container of
A objects (and *i returns an A&), and j
iterates over a container of B objects (and *j
returns a B&). Assignment will have been overridden if
A is a user defined type, but it may not be defined
specifically for the type represented by B, necessitating
invocation of some user-defined conversion function. Another
user-defined conversion function may have to be invoked to
convert the result of the assignment statement (presumably an A&,
but potentially anything) into something that can be converted to
type bool for the "while" condition. Any of
these user defined operators or conversions might throw an
exception -- or any of the functions that they invoke, and so on.
If an exception occurs in this statement, the user may have no
idea where it came from. More importantly, the order of
evaluation of several parts of the above expression is
unspecified. Therefore, if an exception appears, the user may
have a hard time determining the current state of i, or j
or *i.

Finally, as noted in (iii), exceptions can not be ignored.
With older error reporting techniques, nothing but discipline
forced the programmer to check the return value or error code.
Even if you knew errors were possible, you did not have to check
the return value immediately, just as long as it was checked
before it mattered. With exceptions, this is no longer possible.
You have to cope with an exception when and where it occurs, not
when and where it is convenient for you. This is true even if you
do not plan to actually handle the exception.

So, I have concluded that using exceptions effectively is
difficult. Nevertheless, I have also considered the alternatives.
For example, without exceptions it is impossible to return an
error from a constructor or an overloaded operator. This
situation has lead to several ad-hoc schemes. A couple of the
more popular ones are:

 Set an internal error flag. An operator!
function or a conversion to bool or void*
is often provided to simplify testing the state
of the object. Such a scheme can be used to tell
if a constructor (or an operator) succeeded or
failed, but it does not tell anything about why
the failure occurred. For that, a member function
that returns the value of an error code is
usually provided.

 Two-stage
construction. In this scheme, the actual object
constructor only initializes memory. A second
"initialization" function is called
after an object is 'constructed' to do resource
allocation and any other complex operations that
might encounter errors. This scheme does provide
a means to return error indications to the user,
but the user has to know about the need to
initialize the object before it can be used. It
does not work with overload operators.

Since there is no standardization for any of this, it is
difficult to combine libraries that use slightly different
techniques. It also is virtually impossible to use such classes
to instantiate general purpose templates like those that are now
part of the Standard C++ Library. The truth is: without
exceptions, a lot of the features that make C++ so powerful --
constructors, overloaded operators, and templates -- are either
not as robust as we need them to be, or simply can not be used in
situations where good error handling is required.

What follows are some guidelines that I have put together on
how to cope with exceptions (typically these days this topic is
given the broad general title of "exception safety").
These guidelines are general, high level concepts. Complying with
a guideline may involve re-writing a function or major changes to
a class library. After each guideline, I present more specific
tactics which might be used in certain circumstances to satisfy a
guideline. As with most such offerings, every user will have to
evaluate their own circumstances and determine which of these
guidelines and tactics are applicable to their situation. I must
admit that I have come across a couple of situations where issues
about exception handling were approaching the status of religion.
Under the circumstances, I therefore try to offer reasonable
explanations for my recommendations. Nevertheless, my ultimate
goal is more reliable software, not religious discussions. If
other approaches work for you, by all means use them. I remain
interested in hearing from readers who have applied these and
other guidelines on real projects -- successfully or not. The
more collective knowledge we build on this topic, the better off
we will all be.

Terminology and Conventions

In the following, I often mention the "state of an
object". In a separate article [2], Dr. Harald Mueller
presents a more general discussion of the problems I discuss in
guidelines 1 through 3. In his article, Dr. Mueller talks about
"resources". I have decided to stick with the term
"object" since that is the type of resource most C++
programmers are going to be concerned with most of the time
(though I will occasionally use the term "resource
leak"). In Dr. Mueller's article, he defines and discusses
how resources can be in either "good" or
"bad" states. For myself, I originally preferred a
slightly different terminology than Dr. Mueller's. Then along
came the Final Draft of the C++ Standard and introduced its own
terminology. Not surprisingly, the Standard is only interested in
specifying requirements, so while it may be the Standard, a
slightly broader terminology is useful for a more general purpose
discussion. Rather than try to provide solid definitions for such
overloaded terms as "bad", I will try to use terms that
have fairly obvious meanings to most C++ programmers.

I will assume that we all know pretty much what is meant when
we say that an object is in a good state. If we assume
that we start with an object in a good state, and then we invoke
a function that can change the state of the object, and that
function exits with an exception, the question is "what
state is the object left in?" Naturally, the actual state
depends on the object, the function, and the exception, but for
discussion purposes we group all these final states into several
broad categories:

 undefined - when the Standard says that
something is undefined, it means that the details of some
particular behavior are outside the scope of the Standard. The
more broadly understood meaning of "undefined" is that
it "breaks" (the most cynical definition is that it
works in test, but breaks at the customer's). In this context,
when I say that an exception has left an object in an undefined
state, I mean that all future operations on that object yield
undefined behavior, including attempting to destroy the object.

 destructible - Dr. Mueller called this a
"valid shutdown" state, and considered it a subset of
the "bad" states. In the original article, I called
this a "bad" state, but considered it better than being
in an "undefined" state. In either case the point is
the same: if the object is not in a "good" state, but
the destructor of the object can be safely run and the object
destroyed, then it is in a destructible state. As I will discuss
below, this category may cover a lot of states, from just a
"little inconsistent" where many operations might still
work, to "really screwed up" where only the destructor
will work. I decided to switch to the term
"destructible" because that is what we are usually
talking about anyway, and it makes more sense than overloading
"bad", "inconsistent", or
"invalid".

 consistent state - this is a term used in the
Standard. It is the state an object is left in when a function
provides what is called "the Basic Guarantee." I
must admit that I find this terminology slightly confusing. I am
sure that many people beside myself consider
"consistent" to be synonymous with "good".
The Standard makes it clear that you can not assume anything at
all about the actual state of such an object. Nevertheless, there
are often operations that can be applied to an object in a good
state that are independent of what the current state may be --
assignment is a typical example. Furthermore, it is usually
possible to make inquires about the specific state of a good
object. On the other hand, I can easily show examples where it is
possible for an exception to leave an object in an state where
neither assignment nor typical inquiries make sense, yet the
object would still be destructible. I would not consider such an
object to be in a "consistent" state, yet the program
that contained such an object could continue to run correctly. To
my thinking, destructibility is thus the most basic
requirement of an object after an exception. As a result, I have
taken the position that what the Standard refers to as a
"consistent state" is the same as what I call
"destructible." I will elaborate upon my reasons for
this below.

 initial state - in other words, it is as if the
function was never attempted at all. Obviously, this is a good
state for the object (by definition). Equally obvious, this is a
very desirable state to be left in after an exception. The
Standard requirement that a function leave the object in its
initial state after an exception is referred to as "the
Strong Guarantee" This point will be discussed much
further in the guidelines below.

For certain examples in the rest of this article, I use a
Stack template class that was originally presented by Cargill in
[1], and elaborated upon by Dr. Mueller in [2]. The interface for
this class is shown in listing 1. While the Stack class is fairly
simple, it remains a good example of the difficulties inherent in
using exceptions.

A couple of brief notes about coding style: (i)I prefer to use
the constant symbol null to represent the null pointer in
my code instead of the current convention of using 0. I ask the
reader to assume the following declaration exists within the
scope of all the code that follows: const long null = 0L; (ii)I
have adopted a commenting convention of //>x to
indicate places in a function where exceptions are possible. Like
any manual approach, this technique is error prone and very
likely to miss something. It is better than nothing, however, and
I have found it useful.

Table 1 summarizes the guidelines that are presented in the
next section.

Table 1. Guidelines for Coping with Exceptions

1. When you propagate an exception, try to leave the object in
the state it had when the function was entered.

2. If you can not leave the object in the same state it was in
when the function was entered, try to leave it in a good state.

3. If you can not leave the object in a "good"
state, make sure the destructor will still work.

4. Avoid resource leaks.

5. Do not catch any exception you do not have to.

6. Do not hide exception information from other parts of the
program that might need it.

7. Unless you have a Strong guarantee of exception safety,
assume that you must destroy the object after any exception.

8. Always catch an exception by reference.

9. Never depend upon destructors for functionality in any
situation where fault-tolerance is required.

10. Do not get too paranoid.

Catching and Propagationg Exceptions

We must state some assumptions before we actually start to
deal with exceptions. We will assume that an exception thrown by
a lower level routine indicates that the routine failed. In a
follow-on article I present some goals and guidelines for
throwing exceptions. While there is no way to ensure that
exceptions are only used to indicate a failure condition, (and
there are a couple of situations where they may not), it is
probably safe to assume that most programmers are not going to
accept the overhead inherent in an exception throw if other
mechanisms are available to indicate normal completion.

We will also assume that exceptions are typically a rare
occurrence in a well designed program. This means, in general,
that the condition causing the error is not likely to be one that
is easily anticipated and handled by the next higher level
routine. The entire exception handling mechanism is designed to
pass information from the point where an error is detected up the
calling chain to a higher level of the program where enough
information exists to be able to handle the error. In many cases,
exceptions are going to propagate all the way to a human
operator. Therefore, it is probably safe to say that the vast
majority of code will simply propagate exceptions from lower
level routines to higher level ones.

It is this propagation of exceptions that causes the grief and
anguish. The function that throws the exception knows what state
it is in and it chooses when to throw the exception. The function
that finally handles the exception remains in control -- it must
necessarily branch into a separate exception handling path -- but
in handling the exception it recovers from the error and
continues. All of the intervening functions between the one that
originally threw the exception and the one that finally handles
it are a problem. When an exception propagates out of a function,
it indicates that the function failed. It is as if the function
threw the exception, but without any control over where the throw
occurs. The propagating exception terminates the function,
unwinds its stack, and continues on its way.

If not anticipated and handled correctly, this propagation of
an exception can leave dangling resources, objects in
inconsistent or even undefined states, and in general cause more
problems than the original error. Propagating exceptions destroy
otherwise perfectly rational flow of control through a program.
In a very real sense, exceptions are "goto's from
hell". So how do we cope with them?

Guideline 1. - When you propagate an
exception, try to leave the object in the state it had when the
function was entered.

This is the Golden Rule of exception handling. It is not going
to do much good for a program to handle an exception if the
program has gone into an invalid state as a result of the
exception propagating to the point where it could be handled. The
C++ Standard refers to this as "The Strong Guarantee of
Exception Safety". In other words, when a function provides
"the Strong Guarantee", it is assuring its clients that
the function will leave the object unchanged if an exception
occurs.

I personally feel that writing code that meets this goal is
very difficult. In an extreme case, meeting this goal might
involve making a complete copy of the state of an object,
attempting the operation on the copy, and then either reverting
to the original if the operation fails, or removing it and going
forward with the now changed copy as the object's new state.
Nevertheless, I put this guideline first because it is the target
that should always be the ultimate goal. How to go about this is
a complicated subject, and there are more detailed articles on
dealing with it [2]. I will only give a few simple suggestions
here.

Tactic 1a. Make sure your const
functions really are const.

The first step in making sure objects stay in good states is
determining which operations on the object actually change the
state. The C++ language assists us in this regard by requiring
member functions that can be applied to constant objects to be
designated as const functions. As we all know however, const
can be subverted, either by designating some members mutable,
or with a const_cast. If your const functions
really do not change the state of the object, then you can safely
ignore them -- exceptions that propagate from them will not
affect anything.

On the other hand, if your const functions change the
physical state of the object, then you must treat your const
functions like your non-const functions -- only more so. As a
user, I would be very annoyed if I discovered that a const
function could actually corrupt my object under some
circumstances. If you discover such a function in your class, I
might urge reconsidering whether it should be a const
function. An example of this actually occurs in the Standard C++
Library. The basic_string function c_str() is a const
function, yet it appears that an implementation is allowed to
reallocate the internal character array if necessary to make room
for the terminating NULL character. This could throw a bad_alloc
exception. In this case const is taken to mean
"logical const-ness". When a const function
exhibits such behavior, it has numerous ramifications, most of
them bad.

Each of the comments indicates a statement that potentially
can throw an exception. For example, the statement at x1
can throw a bad_alloc exception if there is not enough
memory to satisfy the request. In this statement, the member
variable nelems is increased, and then used as the size of
the new request (a typical C idiom). If this request fails
and throws an exception, it propagates out of push()
leaving nelems set to the new value. The Stack object is
no longer in a good state.

Besides doing memory allocation, statement x1 also uses
T::T(). Similarly, statements x2 and x4 use T::operator=(),
and x3 uses T::~T(). Each of these functions might
throw an exception. For now I will ignore the possibility of
exceptions from destructors, but x1, x2, and x4
are all potential problems. If we propagate an exception from one
of these operations it will indicate that the push()
operation failed, but in what state will we leave the Stack
object?

An exception caused by a failure in T::T() at x1
has the same effect as a bad_alloc exception. If we use a
temporary variable for the new value of nelems, then an
exception from x1 can safely be allowed to propagate. We
will assume that if T::T() throws an exception the runtime
mechanism will destroy all the already constructed elements in
the array and release the allocated memory (this is required by
the Standard).

If one of the assignments in the loop at x2 fails, the
internal state of the Stack object is still good, but we have a
memory leak since the buffer pointed to by new_buffer will
never be deleted. We will discuss resource leaks in more detail
under Guideline 4 below.

If the assignment at x4 fails, variable top will
already have been incremented. In this case, the exception will
indicate that the push() failed, but the internal state of
the object will have been updated as though it succeeded. Again,
we have left the object in an inconsistent state.

In order to make sure that possible exceptions that propagate
out of push() leave the object in its original state (and
do not cause a memory leak), a number of modifications are
necessary:

1. Use a temporary for the new array size.

2. Use a temporary object of the template class auto_deleted_array_ptr
to create an automatic variable that manages the
new buffer.

3. After the new buffer is
initialized, swap ownership of the new buffer
between the auto_deleted_array_ptr object
and the stack.

4. Delay the increment of the top variable
until after the new element is assigned to the
stack buffer.

Class auto_deleted_array_ptr is a variation of the auto_ptr
template class defined in the utilities section of the C++
Standard Library. I discuss auto_ptr and my own variations
more fully under guideline 4 below. For now, the key point is
that an auto_deleted_array_ptr is an object which owns the
array pointer. The destructor of auto_deleted_array_ptr
calls delete[] on the pointer it owns. The header for auto_deleted_array_ptr
is shown in listing 3. Using an auto_deleted_array_ptr
object will solve our memory leak by automatically deleting the
array if an exception occurs. When the new array has been
initialized with copies of the elements from the old buffer (an
exception prone task), the ownership of the new buffer is
assigned to the stack object. Lastly, we re-write the assignment
at x4 so that we do not update the state of the object
member top_ until after the potentially dangerous
assignment has completed. All this gives us:

template<class T>

Stack<T>::push(const T& element)

{

if (top == nelems) {

size_t new_nelems = nelems * 2;

auto_deleted_array_ptr<T> new_buffer =

new T[new_nelems]; //>x1

for (int i = 0; i < top; i++)

new_buffer[i] = v[i]; //>x2

v = new_buffer.reset(v); //3

nelems = new_nelems;

}

v[top] = element; //>x4

top++;

}

The statement at line 3 swaps ownership of the old buffer and
the new buffer. After the statement, the auto_deleted_array_ptr
object owns the old buffer and the Stack object owns the new
buffer (via its member v). The new_buffer object
will then delete the old buffer when it is destroyed at the end
of the block. I do it this way so that if an exception occurs
when the old buffer is deleted, the stack object will still be in
a good state.

Even this simple example illustrates a key difficulty in
coping with exceptions. When writing a function, it is necessary
to decide which operations might cause exceptions and which
operations are exception-safe. Exception specifications (which
are discussed in a later article) can help in this task, but they
are not a complete answer. In particular, template programmers do
not have access to the exception specifications of classes used
to instantiate the template. Once you get into the mind set of
expecting exceptions, the problem often becomes trying to
determine which statements are NOT possible sources of
exceptions. The only operations that can safely be assumed to
never throw exceptions are the basic operations on the built-in
data types. In the example above, the call to auto_deleted_array_ptr::reset()
is safe because all it does is swap two built-in pointer types
through a temporary.

The auto_deleted_array_ptr object automatically deals
with the possible exceptions in the loop at line x2. An
exception prior to line 3 will now propagate out of the function
without causing either a bad state or a memory leak. That leaves
only the possible exception from the assignment at line x4 to
be dealt with, which brings up tactic c.

Side effects are a fact of life in C++. Some of them we can
not see directly (and can not do anything about). Others we can
see. In statement x4, whatever happens inside T::operator=()
are of the former type, top++ is one of the latter. As it
was originally written, an exception from T::opertor=()
was guaranteed to leave us in an inconsistent state (at least)
because top would have been incremented before T::operator=()
was called. Since T::operator=() might throw an exception,
we can avoid the side effect to top by postponing the
increment.

Originally, I thought it made more sense to postpone the side
effect as I show above. In practice this has not turned out to be
such good advice. The problem is that the statements at x4 do not
look right to any experienced C/C++ programmer. This means that
they are forever in danger of being changed by someone doing
maintenance. Considering that exceptions are suppose to be rare
events, I have concluded that it makes more sense to make the
exception safety explicit by actually using a try/catch block in
this case. These days, I would write the last two statements of
the example above as:

try {

v[top++] = element;

} catch (...) {

--top; // reset state on exception

}

Note that even though this makes sure top has the
correct value, we can not be sure what state the Stack object is
in if an exception occurs at line x4 -- it depends upon
what state T::operator=() leaves the object at v[top].
If an exception thrown by T::operator=() leaves the T
object in a good state, then we can say that the Stack object
itself is in a good state. If the assignment leaves the T
object only in a destructible state, then we must consider the
Stack object to also only be in a destructible state -- we can
not expect another call to push() to work if the stack
buffer now contains an inconsistent object. In the final case, if
an exception leaves the T object in an undefined state,
then we must consider the Stack object to be in an undefined
state. This means that it probably will not be possible to
destroy the stack. While there is no way to tell what state the
object at v[top] is in if an exception occurs at line 4,
we want to make sure that if class T adheres to Guideline
1, then the stack object does also.

For a slightly more complicated example consider the Stack::pop()
function. In its original form:

template<class T>

T Stack<T>::pop()

{

if (top == 0)

throw "pop on empty stack";

return v[--top]; //>x1

}

The return statement uses the copy constructor of the
template class T. If T::T(const T&) throws an
exception, it will again do so after top has been changed.
The exception will indicate that pop() failed, but the
internal state will have been changed as though it succeeded.
While it might be argued that the stack is still in a good state
in this case, it does violate our goal -- we want to leave top
as it was when we entered the function. Since we can not
decrement top after the return statement, we must catch
the exception and reset the state (we will also throw a more
appropriate exception for the Stack empty condition):

if (top == 0)

throw domain_error("empty stack");

try {

return v[--top];

} catch (...) {

++top;

throw;

}

The domain_error class is one of the Standard library
exception classes. It is derived from logic_error, which
is in turn derived from exception. As discussed in the
second article of this series, library exceptions should derive
from the standard exception classes. Class domain_error is
a general purpose base class that indicates that the domain of
the attempted operation is invalid. For this example, I chose to
throw a domain_error object directly rather than derive a
new, Stack specific, domain_error subclass.

Guideline 2. If you can not leave the object in a same
state it had when the function was entered, try to leave it in a
good state.

In other words, even if we loose the old state of the object,
we would like to be able to reuse the object. I have come very
close to taking this guideline out of the article on several
occasions. The problem is that if you do not know what the actual
state of the object is after an exception, then there is very
little that you can reasonably do with the object, other than
destroy it. Nevertheless, I have left the guideline in the
article, and in its No. 2 position, simply because it makes sense
as a goal of good class design. In particular, there are often
operations that make sense without knowing the exact state of an
object. A good example of this is the assignment operator.
Typically with assignment we expect to loose the old value of the
object when we assign a new value to it anyway. Consider the
Stack assignment operator (in its initial form):

template<class T>

Stack<T>& operator=(const Stack<T>& s)

{

if (&s == this) return *this;

delete[] v; //>x1

v = new T[nelems = s.nelems]; //>x2

for (top = 0; top < s.top; top++)

v[top] = s.v[top]; //>x3

return *this;

}

We have several possible exception sites in this function. As
before, I will ignore the possibility of exceptions from the
destructors invoked in x1. Line x2 uses the default
constructor for class T, and line x3 uses T's
assignment operator. We can (and will) re-write this function to
meet Guideline 1, but for now observe that if an exception
propagates from line x2 we are in an undefined state (not
even 'destructible'). The old array has been deleted, but an
exception at x2 means we failed to allocate its
replacement. Pointer v is left dangling and if we try to
destroy the Stack object, delete[] will be invoked on this
dangling pointer. If we do nothing else, we want to make sure
that we can always destroy the object (Guideline 3 -- which we
will get to later). This shows just how easy it is for a
propagating exception to leave an object so messed up that it can
not even be safely destroyed.

If we get past line x2 and hit an exception in line x3
we are in a more interesting position. The Stack object is now
actually in a consistent state, but since we only have a partial
copy done, the object is not valid. The exception propagating
from the function will indicate that the assignment failed, but
at this point, since we have deleted the old stack data, we can
not restore the object to the state it had before the function
was called. The only valid things that can be done with
this stack object are to destroy it, or reassign a valid stack to
it. I emphasize the word valid. This is a case where the
internal state of the object appears good, and hence the typical
operations could be invoked and would appear to work, but the
meta-state (for lack of a better term) is inconsistent. In this
case, the stack object does not represent a valid
last-in-first-out ordering of the objects on the stack because it
was truncated in the middle of the copy of a valid stack.

As noted, we can fix the assignment so that it meets Guideline
1, but let us assume that we could not do that.

Tactic 2a. Either reinitialize the object, or mark it
internally to indicate that it is no longer usable but might be
recovered.

Another reason why we want to leave objects in a good state,
if possible, is that our clients might not be paying as much
attention to exception handling as they should. Even though
Guideline 6 (below) emphasizes that the only safe thing to do
after an exception where Guideline 1 is not guaranteed is to
destroy the object, our clients might handle the exception, but
not realize that the object is no longer valid. In such cases, we
want to make sure that any further attempt to use the object will
be rejected.

In the case of our Stack assignment operator, we can simply
reinitialize the object to the empty state. In more complicated
cases, we may want to leave the object in the state it was in
until the user explicitly clears the condition. This makes sure
the user becomes aware of the problem. Rewriting Stack::operator=()
to become

template<class T>

Stack<T>& operator=(const Stack<T>& s)

{

if (&s == this) return *this;

auto_deleted_array_ptr::remove(v); //>x1

top = 0; nelems = 0; // reinitialize object

v = new T[s.nelems]; //>x2

for (size_t i = 0; i < s.top; i++)

v[i] = s.v[i]; //>x3

nelems = s.nelems;

top = s.top;

return *this;

}

Line x1 deletes the array through a temporary (auto_deleted_array_ptr::remove()
is discussed below) to guarantee that the object can be destroyed
in case of an exception. Line x2 is now safe, and any
exception will leave the object in a valid empty state. Likewise,
an exception at line x3. As noted above, we can rewrite
this function to meet Guideline 1, but this is a good start.

Guideline 3. - If you can not leave the
object in a "good" state, make sure the destructor will
still work.

While the ultimate goal is to leave an object in a good state
(Guideline 1 or 2) it may not always be possible (or at least not
practical). As a last resort, we want to leave the object in such
a state that it can always be safely destroyed. Never forget that
as an exception propagates it will unwind the stack frame of
every function it propagates through. Many times this will invoke
the destructor of the very object that threw the exception. One
question that should always be asked when attempting to cope with
exceptions is: "What will happen if this exception attempts
to destroy this object?"

As our original Stack::operator=() function showed, it
is not difficult to leave the object in a state where even the
destructor will not work. The most common cause of this is a
dangling pointer.

In the Stack assignment function above, the simplest solution
to the dangling pointer was to set v to null after
the delete[] statement. Since I figure this is going to be
such a common occurrence, I added a static function to my auto_deleted_ptr
(and auto_deleted_array_ptr) class to facilitate this
operation. Calling auto_deleted_ptr::remove(p) (auto_deleted_array_ptr::remove(p))will delete pointer p through a temporary and leave p
set to null. Even if an exception occurs during the
destructor call, p will not be left dangling. If you use auto_ptr
type objects as members of your class instead of raw data
pointers, the task becomes even easier. If ap is an auto_ptr
object, then:

delete ap.release();

will set ap to null internally before returning
the pointer so it can be deleted.

Guideline 4. - Avoid resource leaks.

The most obvious type of resource leak is a memory leak, but
memory is not the only resource that can leak (I once had a
program that tended to leak TCP/IP sockets). In one sense, a
resource leak is just another example of a inconsistent state. In
this case though, the resource that is in the "bad"
state is the one that has leaked, not the one that did the
leaking. For this reason, I deal with it separately.

It is not possible to discuss exceptions and memory leaks
without some discussion of the auto_ptr<> template
class provided by the Standard C++ Library. Auto_ptr<>
is declared in header <utility>. It is a template
which takes a single template argument of class type.
Conceptually, auto_ptr<> is a very simple class that
has two functions. First, and foremost, it holds a pointer to an
object of its argument type. As the name suggests, auto_ptr<>
objects are intended to be automatic objects. These are the
objects for which the compiler will automatically invoke the
destructors. Such objects include objects which are members of
other objects. When an auto_ptr<> object is
destroyed, it calls delete on the pointer it holds.

The second function that the auto_ptr<> class
provides is the ability to transfer the ownership of a pointer
from one auto_ptr<> object to another. This is
conceptually very useful when you want to allocate an object in
one function but some other function must be responsible for
deleting the object later. Before auto_ptr<>, this
"transfer of responsibility" had to be handled with
documentation and trust. Now you can make explicit in the code
the fact that ownership is being transferred. Unfortunately,
these two tasks of auto_ptr<> are somewhat at odds
with each other, and auto_ptr<> ends up depending
upon a very subtle use of member templates in order to correctly
implement the functionality described here. Most users will not
need to know or care how auto_ptr<> is implemented,
however; they will simply use them and everything will work.

There are a couple of things that will not work with though,
and they are deliberate. The first and most obvious of these is
that auto_ptr<> objects do not meet the requirements
for use in Standard containers, most especially in vector<>.
For this reason, auto_ptr<> is designed so that it
will generate compile errors if you try to instantiate a
container with an auto_ptr<> type. This often
surprises people, but there are good reasons for it (which we
will not go into here), so just accept it. The other thing auto_ptr<>
was not designed to handle is a pointer to an array. Auto_ptr<>'s
destructor calls delete, not delete[]. As you know,
calling delete on a pointer that actually points to an
array of objects results in undefined behavior. Unfortunately,
this usage will go undetected by the compiler.

In the examples below, I show some uses of the standard auto_ptr<>
class. I also show some uses of my own version(s) of auto_ptr<>.
While most users will want to stick with the Standard auto_ptr<>
class, but I decided to do my own version for a couple of
reasons. My primary reason was that none of my compilers actually
provide a "standard" version of auto_ptr<>
yet, so I was going to have to develop my own in any case.
Furthermore, I wanted some functionality in my version of auto_ptr<>
that I knew wasn't in the standard version, and I didn't need
some of the standard auto_ptr<> functionality that
is impossible to implement without member template support.

My version of auto_ptr<> is called auto_deleted_ptr<>
and is shown in listing 2. Like the standard auto_ptr<>
it owns its pointer and deletes it when the auto_deleted_ptr<>
object is destroyed. Unlike the real auto_ptr<>, you
can not use my auto_deleted_ptr<> objects to
transfer ownership of a pointer to another object. My auto_deleted_ptr<>
also differs from auto_ptr<> in how its reset
function works. My version returns the old pointer; the standard
version has a void return and treats reset just
like assignment. Finally, I created a second auto_ptr<>
class to deal with pointers to arrays: auto_deleted_array_ptr<>.
It is just like auto_deleted_ptr<> except that it
calls delete[] instead of delete in its destructor.
Now, what can we do with these auto_ptr<> classes?

Broadly speaking, there are three different instances where
exceptions can cause resource leaks: in a constructor, in a
destructor, and in a function (whether a member of a class or
not). Let us look at constructors first.

Constructors are a special case for the exception handling
mechanism. When an exception propagates from a constructor, the
partial object that has been constructed so far is destroyed. If
necessary, any memory allocated from the free store for the
object is also released. Note that the destructor for the object
itself is not called -- only destructors for any completely
constructed sub-objects. If, during the construction, a resource
(such as memory) is obtained directly and not as part of a
sub-object whose destructor will release the resource, then a
resource leak can occur. For example:

class T1 { ... };

class T2 { ... };

class X {

T1* p1;

T2* p2;

public:

X();

~X();

};

X::X()

{

p1 = new T1;

p2 = new T2; // exception causes leak

}

If an exception is thrown by T2() during the
initialization of p2, then the T2 object will be
destroyed, and the memory obtained by new will be
released, but not the pointer held in p1. We have a memory
leak.

There are a couple of ways this can be dealt with. We could
use a try block to catch the exception and attempt to
release the memory, but if we have several resources allocated
this way, then the nested try blocks can get tedious and
error prone. An alternative is to make sure the pointers are
initialized to null, and then delete them all in the catch
block.

X::X() try :

p1(null), p2(null)

{

p1 = new T1;

p2 = new T2;

}

catch (...) {

delete p2;

delete p1; // reverse order

throw; // redundant

}

This example uses several features from the new Standard, so
do not expect this to work on your compiler yet. Placing the try
keyword immediately after the parameter list (or the exception
specification if one exists), and the catch clauses after
the function body, produces a function-try-block. A
function try block associates a handler with the entire function
body, including the ctor-initializer- list. In this
example, the initializer list is not the concern, but you can see
that the initializer list is within the scope of the try.
With any pointer not set in the constructor body guaranteed to be
null, the catch block can safely invoke delete on
them. All this leads to:

Tactic 4a: If you have raw data pointers as
members, initialize them to null in the initializer list of your
constructor(s), then do necessary allocations in the constructor
body where a catch block can deal with potential resource leaks.

This is one possible way to deal with a potential resource
leak in a constructor. Another technique is to use the
"resource acquisition is initialization" strategy. This
is where auto_ptr<>s come in. In this case, we make
sure that every resource is associated with an object whose
destructor will deallocate it. Applied to our example:

class X {

auto_ptr<T1> ap1;

auto_ptr<T2> ap2;

public:

X();

~X() {};

};

X::X() :

ap1(new T1),

ap2(new T2)

{}

Alternatively:

X::X()

{

ap1.reset(new T1);

ap2.reset(new T2);

}

Now, since ap1 and ap2 are both auto_ptr<>
objects, if an exception occurs trying to initialize ap2,
then the stack unwind will destroy ap1, which will call delete
on the allocated pointer. In this case, our destructor is empty
since the destructors of member objects are invoked
automatically.

Besides having a destructor that will delete the resource,
template auto_ptr<> alsoprovides a release
function which allows you to transfer ownership of a resource
from the auto_ptr<> object to the class. In this
case, we can use an "acquire then transfer ownership"
strategy to give us the following version of X's constructor:

class X {

T1* p1;

T2* p2;

public:

X();

~X() {delete p1; delete p2;}

};

X::X() :

p1(null), p2(null)

{

auto_ptr<T1> t1(new T1);

auto_ptr<T2> t2(new T2);

p1_ = t1.release();

p2_ = t2.release();

}

The auto_ptr<> objects are used to acquire the
resources in a manner that guarantees they will be deleted if an
exception occurs. When all resources have been successfully
acquired, ownership is transferred to the class itself. This is
just Tactic 1b applied to resource acquisition. I see this as a
transitional strategy for constructors, however. In the long run,
I suspect that the use of raw data pointers as class members will
diminish in favor of auto_ptr<> style objects. This
simplifies maintenance as well as the problems of coping with
exceptions. If you worry about performance, keep in mind that all
the operations of auto_ptr<> are inline functions.
Most of these are one line functions that any decent compiler
should have no trouble handling.

Resource allocations in ordinary functions can also cause
resource leaks. Whereas a constructor is building an object and
the goal is to make sure everything already acquired is released
if an exception occurs, a function usually obtains a resource for
internal use and releases it upon completion. If an exception
happens after the resource is acquired but before it is released,
then we have a leak. Under the discussion for Tactic 1b we used
an auto_deleted_array_ptr object in Stack::push()
to manage the new buffer while it was being initialized. We can
do the same thingin Stack::operator=():

template<class T>

Stack<T>& Stack::operator=(const
Stack<T>& rhs)

{

if (this == &rhs) return *this;

auto_deleted_array_ptr<T> new_buffer =

new T[rhs.nelems]; //>x1

for (int i = 0; i < rhs.top; i++)

new_buffer[i] = rhs.v[i]; //>x2

v = new_buffer.reset(v); // swap ownership

nelems = rhs.nelems;

top = rhs.top;

return *this;

}

Here I have to use an auto_deleted_array_ptr<>
instead of an auto_deleted_ptr<> (or a plain auto_ptr<>).
Since I am using my own class, I take advantage of the fact that reset
returns the current pointer to effect an ownership swap idiom. If
we were dealing with ordinary objects instead of arrays, and if v
were an auto_ptr<> object instead of a raw pointer,
we would use the Standard template function swap() to
perform an exchange of ownership as in:

swap(v, new_object);

Finally, we can have resource leaks in destructors. As a
general rule, we do not want to throw (or propagate) exceptions
from destructors. Nevertheless, we can not always prevent it, so
let us take a look at a simple problem. Consider our first
example of class X above. We have two pointers to two
different types of objects. Everything has gone well, and now the
destructor of X is invoked. It is pretty simple.

X::~X() { delete p2; delete p1; }

Not much to go wrong here, but assume that it does -- say the
T2 object throws an exception when deleted. Like constructors,
destructors are special functions for the exception runtime
mechanism. When an exception occurs in a destructor, it is
treated like an exception in a constructor, i.e. all complete
sub-objects that still exist are destroyed (in reverse order of
their construction) and then the memory deallocation function is
called, if needed. Exactly the same problem occurs here as it
does in a constructor. The exception from the destructor of T2
will terminate the body of the destructor without deleting the
resource held by p1. The solutions are likewise similar to
those applied to a constructor.

For example, we can attempt to catch the exception and
guarantee that other resources are deleted. Unlike the
constructor however, there is no way to organize things so we can
cover everything in a single catch clause -- at least not without
more work than is worth it. Once again, we turn back to the
"acquisition is initialization" strategy and use auto_ptr<>
objects. If all our member variables are auto_ptr<>
objects then our destructor is empty anyway, so let us assume
otherwise.

X::~X()

{

auto_ptr<T1> t1(p1);

auto_ptr<T2> t2(p2);

}

This may seem a little silly, but it does work. We transfer
ownership of the resources to the two temporary objects (we
create them in the same order that p1 and p2 are
declared so they will be destroyed in reverse order). When the
destructor body exits, these objects are destroyed, deleting
their pointers. If an exception occurs in the destruction of t2,
then the stack unwind will still destroy t1.

Guideline 5. - Do not catch any
exception you do not have to.

There is an old C rule of error handling (actually it probably
goes back to the Countess Ada Lovelace) that states: "do not
test for any error condition you do not know how to handle."
Like most cynical proverbs, there is a certain amount of wisdom
in this. In C++, we now have a standard way to handle any error
condition -- throw an exception -- but this just shifts the
burden of the problem. The basic truth remains: it is a waste of
time (yours and the computers) to catch an exception you do not
know how to handle.

Tactic 5a: Rewrite functions to preserve state,
if possible.

As we have seen in the discussions above, when an exception
propagates upward from a lower level function, we may have to
catch it just to reset the state of our object. The point of
Guideline 5 is that we want to avoid this as much as we can.
There may be mitigating circumstances, however. Consider our
rewritten push() function. The last two lines could read:

v[top] = element;

++top;

This deliberately moves the incrementing of top after
the possible exception from the assignment operator. We could
write this like:

try {

v[top++] = element;

} catch (...) {

--top; // reset state

throw;

}

This is similar to what we have to do in the pop()
function. In that case we do not have any choice; in push()
we do. Guideline 5 says that if we can arrange our functions so
that we can avoid writing try/catch blocks then we should do so.
It leaves us with cleaner code and it is more efficient to let
the exception just propagate than it is to catch and rethrow it.

There is a potential downside with doing this however, when we
write a try/catch block, it is obvious that there is the
possibility of an exception. If we write:

v[top] = element;

++top;

instead of

v[top++] = element;

it is not clear that the first form is necessitated by the
possibility of an exception. We run the risk that during
maintenance, some experienced C programmer (but new C++
programmer) will take a look at the two lines and decide that
whoever wrote them did not appreciate the compactness of
expression possible in C++ and change them back into the latter.
This may turn out to be one of the most annoying drawbacks to
using exceptions in C++, i.e. a great many of the cherished
idioms from C are going to have to be discarded. Instead, we are
going to have to use a much more deliberate (and verbose) style
that allows us to maintain better control over the state of our
objects in the presence of exceptions.

In this particular case, we may actually prefer to leave the
statement in its original form and make our exception handling
explicit

try {

v[top++] = element;

} catch (...) {

--top;

throw;

}

We have to be very careful doing this, however. Remember that
the assignment statement is actually a function call. Broken out,
it looks something like this:

v[top++].operator=(element)

The language rules guarantee that all side effects (in this
case top++) have taken place before the function is entered. In
this case, we know that element is bound to a reference of
the same type, which is an exception safe operation, so we know
for certain that top will have been incremented if an
exception occurs from within the assignment function. Now
consider the more general case of

v[top++] = x;

where x is not the same type as T, but is a type
which can be converted to a T. Now the function breakdown
looks like

v[top++].operator=(T(x));

Now, besides the possibility of an exception from T::operator=(),
we might also have the possibility of an exception from the T(x)
constructor (or from X::operator T(), whichever is
actually used to do the conversion). The language rules allow the
compiler to perform the top++ operation before or after it
creates the temporary T object. Now if we try to write

try {

v[top++] = x;

} catch (...) {

--top;

}

we have no guarantee that top actually was incremented
before the exception occured.

Generally, I still think you are better off rearranging code,
but that means in order to avoid the maintenance problem, we must
document every place in our code where we think an exception is
possible, and especially everywhere we have changed code to make
it exception safe. I use comments of the form //>x.
This is not a particularly good solution, but it is better than
nothing.

Tactic 5b. - Always use a catch(...) block to
cope with propagating exceptions.

If we follow Tactic 5a we will avoid catching exceptions if we
can, but there will be times when it can not be avoided, or the
work necessary to avoid it is excessive. If we conclude that an
exception might occur, and that it could leave us in an
inconsistent state, then we want to make sure we catch that
exception. Since we are not concerned with handling the
exception, only with protecting the state of the object, then the
type of the exception does not matter. Therefore we use a catch
block with a single handler:

catch (...) { /* ... */ }

This is pretty obvious for a template class like Stack, where
we have no idea what kind of exception might be thrown by T,
but in general, even if we know (or think we know) what the
actual exception type is, if we are not actually handling the
exception, then we should use a catch (...) clause.

Tactic 5c. - Do not "handle" any
exception that can not be "fixed."

This tactic falls between those for just coping with
propagating exceptions and those for handling exceptions. I put
it here since it is a special case of the more general Guideline.
The idea is to avoid getting yourself into an infinite loop where
you "handle" an exception without fixing the problem
(usually by just ignoring it), and then retry the operation
again. This is especially a problem when your code depends upon
user intervention to actually "fix" the problem. There
are few things in life more frustrating than computers that tell
you to do something that you either can not or do not want to do,
but refuse to let you out of the mode they are in unless you
comply with their instructions. If you put error messages up to
the user, be sure your dialog box allows them to cancel the
attempted operation as well as retry it. If you do retrys
internally, make sure you put a limit on the number of retrys you
attempt before giving up and propagating the exception to a
higher level.

Tactic 5d. - If you get stuck, call terminate().

This is not really about handling exceptions, but it does have
something to do with coping with errors. There are places where
throwing an exception does not make sense, and the only
possibility left is to end the program. An obvious circumstance
where this happens is in a user defined version of unexpected_handler.
Unexpected_handler must either throw an acceptable
exception, or end the program.

Before exceptions, the normal way to abnormally terminate a
program was by calling the C library function abort(). In
the new C++ world, we should call terminate() instead. Terminate()
just calls terminate_handler. In the default case,
this calls abort(), but the user can replace the default terminate_handler
with a program specific version. So call terminate()
to allow any user defined terminate_handler to run.

Guideline 6. Do not hide exception
information from other parts of the program that might need it.

Just to reiterate the obvious: the purpose of exceptions is to
pass information from the point where an error is detectable to a
point where the error can be handled. If you throw a different
exception rather than rethrowing the original exception, you want
to make sure you are increasing the the possibility of the
exception being handled by doing so.

I will predict that this will probably be the most common
exception handling mistake. Once you have reset your state in a catch
(...) clause, you want to be sure that you rethrow the
exception so that higher level routines will have a chance to
handle it. A failure to rethrow the exception means you have
"handled" the exception -- which is probably not what
happened at all. Just for a reminder, the statement to rethrow an
exception is:

throw;

Tactic 6b. - Rethrow a different exception only
to increase the level of abstraction or capability.

If forgetting to rethrow the exception from a catch (...)
clause is likely to be the most common exception handling
mistake, rethrowing a different, but meaningless, exception will
probably be second. Consider the following:

void foo() try

{

// do something

}

catch (PrivateExceptionType& ex) {

// clean up

}

catch (...) {

// clean up

throw exception("Unknown exception in foo");

}

At first reading this seems to make sense. Foo()
invokes some operation that might throw a private exception type,
which is caught and handled. To be on the safe side, foo()
includes a catch-all block just to cover the bases. The problem
is that when foo() throws the generic exception in the
catch-all block, it effectively destroys all the information in
the original exception. This information presumably indicated
what the original error was and might have been of use to a
higher level routine that was willing and prepared to deal with
an error of that type. Now, all that propagates upward is a
self-fulfilling prophecy, and the program will probably terminate
because nobody knows how to handle an "unknown
exception".

In our ongoing Stack example, there are several places where
we use the new statement. Under the C++ Standard, new
will throw a bad_alloc exception if the memory allocation
fails. In the second part of this series of articles we add a
private exception class to Stack that is derived from bad_alloc.
In Stack<T>::AllocationError we include some
additional information besides that available from bad_alloc
alone, including the new size of the stack being allocated.

In order to be able to throw Stack<T>::AllocationError
we use the nothrow() placement form of new as
in:

new_buffer = new (nothrow()) T[new_nelems];

if (new_buffer == null)

throw Stack<T>::AllocationError(new_nelems);

Class nothrow is defined in the Standard header file <new>
along with the placement form of operator new used
above. When this form is used, operator new reverts to the
classic behavior and returns a null pointer if the allocation
fails. This is the preferred way to do this. Note that this form
only eliminates the possible exception from operator new,
an exception can still be thrown by the constructor of the object
in the new expression. For that reason, we want to be
careful NOT to do the following:

try {

new_buffer = new T[new_nelems];

} catch (...) { // catch bad_alloc from new

throw Stack<T>::AllocationError(new_nelems);

}

In this case, our catch (...) clause will not only
catch the bad_alloc exception as the (erroneous) comment
indicates, but we will also catch any exceptions thrown by the T::T()
constructor. If the new expression fails because of an
exception in the latter, we will hide that exception with our
version of bad_alloc. While it is true that either case
will indicate that the function failed, hiding the original
exception means that any higher level routine that handles the Stack<T>::AllocationError
exception will be solving the wrong problem.

Tactic 6c. - Make sure one catch block does not
hide another.

When the exception runtime attempts to locate a handler for an
exception, it tries the handlers in order. Unlike function
overloading, where the best match from a set of possible
functions is found, an exception handler is found on a first
match basis. This means that you always want to put more specific
handlers before more general handlers. Handlers specifying base
classes (or references to same) must come after those for more
derived classes. Obviously, any catch (...) clause must be
the last handler of a sequence. The compiler will almost
certainly complain if the catch (...) clause is not the
last one, but it may not generate an error message for other
incorrect orderings, so beware.

Guideline 7. Unless you have a Strong
Guarantee of exception safety (Guideline 1), assume you must
destroy the object after any exception.

Now we start getting into guidelines (and tactics) for
actually handling exceptions. Ideally, when you handle an
exception, you correct whatever was wrong and then redo the
failed operation. In order for this to work, you have to be sure
that the operation(s) which failed left their object(s) in the
state they had before the operation(s) was attempted. This is
Guideline 1, and is what the C++ Standard calls the "Strong
Guarantee". If you can not say for certain that Guideline 1
is being followed, then all bets are off. If Guideline 2 has been
followed, you might be in a position to reuse the object, but in
general the only really safe thing to do is destroy the object
and start over.

Consider our stack<> example. Suppose we have a function
that reads some input from a user, parses that input, and does
something with the result. Suppose further that we use a
stack<> to hold the parsed result. We might start out with
something like this

void do_something() {

string in_str;

stack<X> results;

// prompt the user

cin >> in_str;

parse_it(in_str, &results);

// do something with the results

}

Now consider exceptions from parse. Suppose we decide that we
will "handle" the possible exceptions by issuing an
error message and having the user retry. We might start out with
just a minimal try/catch block inside a loop

for (;;) try {

cin >> in_str;

parse(in_str, &results);

} catch (...) {

cout << "Error, please try again\n";

}

There are several things wrong with this approach. Obviously,
we need to catch only the exception(s) we know about (Guideline
6), but I use the catch-all form for simplicity. Also, this is a
good example of the point in Tactic 5c where there is no way out
of the loop if an exception occurs. Finally, I do not necessarily
recommend this coding style. I have adopted it on occasion since
I find that it clearly indicates that the for loop is
intimately connected with the try/catch. The real point in
this case is that we are attempting to re-use both the stack<>
and the string objects after we have an exception.
Since parse() has a prototype of

void parse(const string&, stack<X>*)

we are probably safe in re-using the string object (see
Tactic 1a). The stack<> is another story. If parse()
guarantees to follow Guideline 1 (The Strong Guarantee), then
everything is fine. Of course, for parse() to be able to
make this guarantee, it depends upon everything that it uses,
including all string and stack<> functions,
also providing the Strong Guarantee. Since we are assuming that
not all of our stack<> functions provide this
guarantee, we can not assume that parse() does either. So
we come to the following tactic.

By putting the declarations of in_str and results
inside the try/catch block, we guarantee that they will be
destroyed by the stack unwind BEFORE the catch clause is entered.
We might want to keep in_str outside of the loop if we
want to have the user's input still available inside the catch
clause.

Tactic 7b. Explicitly destroy and re-construct
objects which might be damaged by exceptions.

There might occasionally be times when you can not afford to
have everything inside the try block. Suppose that our do_something
function already contained a loop getting input from the user.
Because of the overhead of destroying one stack and then
allocating a new one on each iteration of the loop, we might
prefer our function to have the following form

string in_str;

stack<X> results;

for (;;) try {

cin >> in_str;

if (in_str.empty()) break;

parse(in_str, &results);

// do something with the results

} catch (...) {

cout << "Error: please retry\n";

(&results)->stack<X>::~stack<X>;

new(&results) stack<X>;

}

This uses the explicit destructor call mechanism to destroy
the object in place, and then uses the placement form of operator
new to reconstruct the object. Obviously this is really ugly
compared to just letting the compiler handle it, so I consider
this a last resort approach. Nevertheless, keep it in mind. It
beats having your program blow up because you tried to reuse an
object that was in an inconsistent state.

Guideline 8. Always catch an exception
by reference.

I have included this guideline mostly as a reminder. It was
not in the original article primarily because I just assumed that
everyone already knew this. That is probably a valid assumption,
but it doesn't hurt to be reminded. When a catch clause is
entered, the exception mechanism will initialize the parameter of
the catch clause from the actual exception in much the same way
as the parameters to a function are initialized from the real
arguments. If your parameter type is a real object, then the copy
constructor will be invoked to do the initialization. Besides the
extra overhead, this also means that if your catch clause
specifies a base type of the actual exception type, the copy
constructor will 'slice' the actual exception to just the base
class component. To restate the obvious, if you have

catch (exception ex) {

cerr << ex.what();

}

and someone throws a domain_error (which is derived
from exception), this catch clause will catch the domain_error,
but when the clause is entered, the domain_error will have
been sliced to just be an exception. This will cause the
output to be whatever implementation specific string is generated
by exception::what() instead of the string in the domain_error
object. Catching by reference

catch (exception& ex) {

cerr << ex.what();

}

means that the call to what() is a virtual function
call, and it yields the string used to create the domain_error
object.

Guideline 9. Never depend upon
destructors for functionality in any situation where
fault-tolerance is required.

As a general rule, we do not want exceptions propagating from
a destructor. There are two reasons for this. First, there is
simply a semantic problem with deciding what it means for a
destructor to have an exception. For ordinary functions, an
exception typically means the function failed. What does it mean
when a destructor fails? Certainly, it should not mean that the
object is left in a usable state since it was not going to be
usable if the destructor succeeded. In most cases the memory for
the object will disappear anyway, whether the destructor succeeds
or not. In other words, there is not much we can do about
handling an error that occurs in a destructor, and we ordinarily
want the destructor to finish anyway, so there is not much point
in signaling errors from a destructor, even though exceptions
give us a way to do this.

The second reason for avoiding exceptions in destructors is
more fundamental - they are likely to abort the program. In a
sense, destructors are part of the exception handling mechanism
itself. Destructors are invoked by the exception handling runtime
as part of the stack unwind process. If a destructor throws an
exception during an exception stack unwind, then the Standard
says the exception handling mechanism gives up and calls terminate().
For this reason, other writers have taken the position that
destructors should never throw or propagate exceptions[2,3]. I do
not go quite that far, but the following tactics should apply
under most circumstances.

Tactic 9a. Do not throw exceptions from a
destructor body.

This is the principal behind Guideline 5 again. Clearly, there
is no sense in testing for error conditions in a destructor body
unless they can be handled right then and there -- otherwise
ignore them (or call terminate()).

Tactic 9b. Do not arbitrarily handle all
exceptions propagating from a destructor.

Exceptions can still legitimately occur when a destructor
calls an ordinary function that throws an exception. It is
tempting to suggest that destructors should not call such
functions, or to say that every destructor should handle its own
exceptions -- tempting, but unrealistic. The first is
impractical, and the second leads to constructs such as

catch (...) {}

which I object to on esthetic grounds. On the other hand, are
there ever legitimate occasions when we might want to propagate
an exception from a destructor? Consider the following real-world
problem:

A program captures medical images from a Computed Radiography
(CR) scanner and transfers them over a fiber optic network to a
central image server. If there is any problem with the network,
or with the image server, the program is required to save the
image to local disk, generate a message on both the local console
and the system administrator's console (a separate network is
available for system administration tasks), and enter an error
state that prevents any more images from being captured until the
problem is fixed and the image saved on disk is transferred to
the central server. This is necessary because the X-ray plates
used by a CR machine are erased by the scan -- once scanned, the
image must be captured or it is lost forever. Assume the network
connection is encapsulated as an object (call it an endpoint). If
the endpoint destructor is called, and the connection is still
open, the destructor will attempt to close it. If some error
occurs it seems reasonable, given the nature of this program,
that an exception should be thrown to indicate a network problem
to higher levels.

This would seem like a perfect case for having a destructor
throw an exception, but maybe not. A closer look at this program
reveals that under normal circumstances the network connection
should always be closed before the endpoint is destroyed. If the
connection is still open when the destructor is invoked, it
probably means the endpoint is being destroyed as a result of
some other exception. If this is the case, then the last thing we
want to do is throw another exception. In fact, we may be trying
to destroy the endpoint as a result of a stack unwind that
resulted from an exception thrown by another network function
call. We want this exception to propagate.

So back to the question of whether a destructor should handle
all possible exceptions or allow them to propagate. Destructors
being the type of functions they are, it may make sense to ignore
a lot of errors in destructors (i.e. catch the exceptions and not
rethrow them). Nevertheless, at some point in coding a class or
library, we have to acknowledge that our users will probably have
a better view of the big picture than we do. There has to be a
limit on how much we try to handle at a given level. This is
especially true for destructors. The person who uses our classes
has to accept some responsibility for how they are used. This
means that any program that has to be fault tolerant must be
designed and coded with special care regarding the types of
exceptions it has to guard against.

If our medical imaging program is written correctly, then it
will not depend upon destructors for normal functionality. The
remote file will always be closed before the file object is
destroyed; the network connection will always be closed before
the network object is destroyed, etc. Exceptions are possible --
in fact the program may depend upon them -- but their occurrence
will be anticipated as part of normal design, and they will be
handled outside of the destructors. We are left with something of
a paradox: the more fault tolerant a program has to be, the less
likely it is going to have to worry about exceptions from
destructors.

As noted above, I object to constructs such as

catch (...) {}

on esthetic grounds alone, but there is a practical aspect as
well. Templates are but one example of a case where we have no
idea what kinds of exceptions are possible from the operations
being invoked on the objects used to instantiate the template.
There are times when we have to assume that the user knows what
she is doing and stay out of the way. This goes even for
exceptions propagating from destructors.

Guideline 10. - Do not get too paranoid.

As a final point, I have noted that it is possible to get
paranoid when trying to deal with exceptions. Maybe this is only
a problem for me because I have spent so much time lately
worrying about exceptions. Still, I am willing to bet that other
people are also going to run into this.

When you start trying to deal with every possible exception
that might occur, you can get into a situation where you either
have to assume that an operation will work, or call terminate().
Consider what happens to our Stack if we try to make sure that an
exception in push() always leaves the Stack in a good
state. As we discussed under Tactic 1c, if the assignment in the
final step of push fails, and T::operator=() does
not leave the element at v[top] in a good state, then our
Stack is not in a good state. We might try to guarantee that the
Stack is always left in a good state like this:

try {

v[top] = element;

++top;

} catch (...) {

v[top].~T(); // destroy the 'bad' object

new (&v[top]) T(); // create new 'good' object

throw;

}

If the constructor in the catch clause throws an exception,
then we have left our Stack object in an undefined state. After
destroying the object at v[top] we can not even expect the
destructor for Stack to work. We must assume that the constructor
will be able to reinitialize the object at v[top].

If you find yourself writing code like this, take it out. As
we discussed in the beginning, exception handling is something
that must pervade the entire program. At some point you have to
turn the problem over to the user. Ultimately, only the
application developer is in a position to really decide how to
deal with certain errors. More often than not, the application
will have to seek help from a human operator. In the final
analysis, often the best we can do in handling an exception is to
make sure our software stays together, and then propagate the
exception and let a higher level deal with it.

Conclusions

As shown in this article, exceptions raise many issues that
either were not there before, or which had much simpler
solutions. Many of these Guidelines and Tactics can be
illustrated with simple examples, but are not simple to apply in
real practice.

The good thing about exceptions is that not every program
needs to be truly fault tolerant -- in fact, very few do. What is
much more important is that a program be robust. A robust program
is resistant to errors -- it either works correctly, or it does
not work at all; whereas a fault tolerant program must actually
recover from errors. There are obviously different levels of
"robustness." For years, I have liberally sprinkled my
code with "assert" statements to make them more robust.
Since all an assert statement usually does is print a message and
abort the program, there are those who might question whether
this actually qualifies as an improvement in robustness.
Certainly, this is not acceptable behavior in a shipping
application. For this reason, assert statements are usually used
only for debugging and disabled in the final build of an
application.

Before exceptions, the only alternative to a deliberate abort
was to return error flags and check them religiously. Exceptions
provide a third possibility. If an exception is not handled at
some point in a program, it will propagate out of function main()
and invoke terminate(). Thus, an un-handled exception has
much the same effect as an assert statement. Conversely, the
program can choose to handle the exception (if only to provide a
more graceful exit). For this reason alone, programmers are going
to insist upon throwing exceptions, especially in library code --
it relieves them of the problem of deciding whether to abort the
program, or not. As noted in the beginning, throwing exceptions
is easy, coping with them is difficult.

The simplest way to start out coping with exceptions is to
have a single try/catch block in the main()
function to provide a meaningful error message and a graceful
exit of the program (Guideline 5 applied to the entire program).
A program written this way may not be any more robust than a
properly written C program, but that level of robustness will
have been obtained with a lot less effort (virtually none), and a
lot less code. Just think of all the if statements that
will not have to be written.

From that point, developers can start to actually apply these
guidelines to libraries and functions in order to bring them up
to a point where they can be used in a fault tolerant program. We
have to be careful not to rush into using catch clauses to
handle exceptions or we run the risk of falling back into the
problem where we think the program is running fine but in fact it
has entered an erroneous state. Nevertheless, when used
correctly, exceptions can definitely improve the reliability of
our programs. As the examples in this paper have shown, this may
take a lot of work, but developing reliable software usually
does.