FAQs in section [17]:
[17.1] What are some ways try / catch / throw can improve software quality?
By eliminating one of the reasons for if statements.
The commonly used alternative to try / catch / throw is to
return a return code (sometimes called an error code)
that the caller explicitly tests via some conditional statement such
as if. For example, printf(), scanf() and
malloc() work this way: the caller is supposed to test the
return value to see if the function succeeded.
Although the return code technique is sometimes the most appropriate error
handling technique, there are some nasty side effects to adding unnecessary
if statements:
- Degrade quality: It is well known that conditional
statements are approximately ten times more likely to contain errors than any
other kind of statement. So all other things being equal, if you can
eliminate conditionals / conditional statements from your code, you will
likely have more robust code.
- Slow down time-to-market: Since conditional statements
are branch points which are related to the number of test cases that are
needed for white-box testing, unnecessary conditional statements increase the
amount of time that needs to be devoted to testing. Basically if you don't
exercise every branch point, there will be instructions in your code that will
never have been executed under test conditions until they are seen by
your users/customers. That's bad.
- Increase development cost: Bug finding, bug fixing, and
testing are all increased by unnecessary control flow complexity.
So compared to error reporting via return-codes and if, using try /
catch / throw is likely to result in code that has fewer bugs, is less
expensive to develop, and has faster time-to-market. Of course if your
organization doesn't have any experiential knowledge of try / catch /
throw, you might want to use it on a toy project first just to make sure you
know what you're doing you should always get used to a weapon on the
firing range before you bring it to the front lines of a shooting war.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.2] How can I handle a constructor that fails?
Throw an exception.
Constructors don't have a return type, so it's not possible to use return
codes. The best way to signal constructor failure is therefore to throw an
exception. If you don't have the option of using exceptions, the "least bad"
work-around is to put the object into a "zombie" state by setting an internal
status bit so the object acts sort of like it's dead even though it is
technically still alive.
The idea of a "zombie" object has a lot of down-side. You need to add a query
("inspector") member function to check this "zombie" bit so users of your
class can find out if their object is truly alive, or if it's a zombie (i.e.,
a "living dead" object), and just about every place you construct one of your
objects (including within a larger object or an array of objects) you need to
check that status flag via an if statement. You'll also want to add
an if to your other member functions: if the object is a zombie, do a
no-op or perhaps something more obnoxious.
In practice the "zombie" thing gets pretty ugly. Certainly you should prefer
exceptions over zombie objects, but if you do not have the option of using
exceptions, zombie objects might be the "least bad" alternative.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.3] How can I handle a destructor that fails?
Write a message to a log-file. Or call Aunt Tilda. But do not throw
an exception!
Here's why (buckle your seat-belts):
The C++ rule is that you must never throw an exception from a destructor that
is being called during the "stack unwinding" process of another exception.
For example, if someone says throw Foo(), the stack will be unwound
so all the stack frames between the throw Foo() and the
} catch (Foo e) { will get popped. This is called
stack unwinding.
During stack unwinding, all the local objects in all those stack frames are
destructed. If one of those destructors throws an exception (say it
throws a Bar object), the C++ runtime system is in a no-win situation:
should it ignore the Bar and end up in the
} catch (Foo e) { where it was originally headed? Should
it ignore the Foo and look for a } catch (Bar e) {
handler? There is no good answer either choice loses information.
So the C++ language guarantees that it will call terminate() at this
point, and terminate() kills the process. Bang you're dead.
The easy way to prevent this is never throw an exception from a
destructor. But if you really want to be clever, you can say never
throw an exception from a destructor while processing another
exception. But in this second case, you're in a difficult
situation: the destructor itself needs code to handle both throwing an
exception and doing "something else", and the caller has no guarantees as to
what might happen when the destructor detects an error (it might throw an
exception, it might do "something else"). So the whole solution is harder to
write. So the easy thing to do is always do "something else". That
is, never throw an exception from a destructor.
Of course the word never should be "in quotes" since there is always
some situation somewhere where the rule won't hold. But certainly at least
99% of the time this is a good rule of thumb.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.4] How should I handle resources if my constructors may throw exceptions?
Every data member inside your object should clean up its own mess.
If a constructor throws an exception, the object's destructor is not
run. If your object has already done something that needs to be undone (such
as allocating some memory, opening a file, or locking a semaphore), this "stuff
that needs to be undone" must be remembered by a data member inside the
object.
For example, rather than allocating memory into a raw Fred* data member, put
the allocated memory into a "smart pointer" member object, and the destructor
of this smart pointer will delete the Fred object when the smart pointer
dies. The template std::auto_ptr is an example of such as "smart pointer."
You can also write your own reference counting smart
pointer. You can also use smart pointers to
"point" to disk records or objects on other machines.
By the way, if you think your Fred class is going to be allocated into a
smart pointer, be nice to your users and create a typedef within your Fred
class:
#include <memory>
class Fred {
public:
typedef std::auto_ptr<Fred> Ptr;
...
};
That typedef simplifies the syntax of all the code that uses your objects:
your users can say Fred::Ptr instead of std::auto_ptr<Fred>:
#include "Fred.h"
void f(std::auto_ptr<Fred> p); // explicit but verbose
void f(Fred::Ptr p); // simpler
void g()
{
std::auto_ptr<Fred> p1( new Fred() ); // explicit but verbose
Fred::Ptr p2( new Fred() ); // simpler
...
}
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.5] How do I change the string-length of an array of char to prevent memory leaks even if/when someone throws an exception?
If what you really want to do is work with strings, don't use an array of
char in the first place, since arrays are evil.
Instead use an object of some string-like class.
For example, suppose you want to get a copy of a string, fiddle with the copy,
then append another string to the end of the fiddled copy. The array-of-char
approach would look something like this:
void userCode(const char* s1, const char* s2)
{
char* copy = new char[strlen(s1) + 1]; // make a copy
strcpy(copy, s1); // of s1...
// use a try block to prevent memory leaks if we get an exception
// note: we need the try block because we used a "dumb" char* above
try {
...insert code here that fiddles with copy...
char* copy2 = new char[strlen(copy) + strlen(s2) + 1]; // append s2
strcpy(copy2, copy); // onto the
strcpy(copy2 + strlen(copy), s2); // end of
delete[] copy; // copy...
copy = copy2;
...insert code here that fiddles with copy again...
}
catch (...) {
delete[] copy; // we got an exception; prevent a memory leak
throw; // re-throw the current exception
}
delete[] copy; // we did not get an exception; prevent a memory leak
}
Using char*s like this is tedious and error prone. Why not just use an
object of some string class? Your compiler probably supplies a string-like
class, and it's probably just as fast and certainly it's a lot simpler and
safer than the char* code that you would have to write yourself. For
example, if you're using the std::string class from the standardization committee, your code might look something like this:
#include <string> // Let the compiler see std::string
void userCode(const std::string& s1, const std::string& s2)
{
std::string copy = s1; // make a copy of s1
...insert code here that fiddles with copy...
copy += s2; // append s2 onto the end of copy
...insert code here that fiddles with copy again...
}
The char* version requires you to write around three times more code than
you would have to write with the std::string version. Most of the savings
came from std::string's automatic memory management: in the std::string
version, we didn't need to write any code...
- to reallocate memory when we grow the string.
- to delete[] anything at the end of the function.
- to catch and re-throw any exceptions.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.6] What should I throw?
C++, unlike just about every other language with exceptions, is very
accomodating when it comes to what you can throw. In fact, you can throw
anything you like. That begs the question then, what should you throw?
Generally, it's best to throw objects, not built-ins. If possible, you should
throw instances of classes that derive (ultimately) from the
std::exception class. By making your exception class inherit
(ultimately) from the standard exception base-class, you are making life
easier for your users (they have the option of catching most things via
std::exception), plus you are probably providing them with more
information (such as the fact that your particular exception might be a
refinement of std::runtime_error or whatever).
The most common practice is to throw a temporary:
#include <stdexcept>
class MyException : public std::runtime_error {
public:
MyException() : std::runtime_error("MyException") { }
};
void f()
{
// ...
throw MyException();
}
Here, a temporary of type MyException is created and thrown. Class
MyException inherits from class std::runtime_error which
(ultimately) inherits from class std::exception.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.7] What should I catch?
In keeping with the C++ tradition of "there's more than one way to do that"
(translation: "give programmers options and tradeoffs so they can
decide what's best for them in their situation"), C++ allows
you a variety of options for catching.
- You can catch by value.
- You can catch by reference.
- You can catch by pointer.
In fact, you have all the flexibility that you have in declaring function
parameters, and the rules for whether a particular exception matches (i.e.,
will be caught by) a particular catch clause are almost exactly the same as
the rules for parameter compatibility when calling a function.
Given all this flexibility, how do you decide what to catch? Simple: unless
there's a good reason not to, catch by reference. Avoid catching by value,
since that causes a copy to be made and the copy can have
different behavior from what was thrown. Only under very special
circumstances should you catch by pointer.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.8] But MFC seems to encourage the use of catch-by-pointer; should I do the same? Updated!
[Recently changed rand() to rand() >> 8 to (typically) improve the period of lowest 2 bits (in 3/06). Click here to go to the next FAQ in the "chain" of recent changes.]
Depends. If you're using MFC and catching one of their exceptions, by all
means, do it their way. Same goes for any framework: when in Rome, do as the
Romans. Don't try to force a framework into your way of thinking, even if
"your" way of thinking is "better." If you decide to use a framework, embrace
its way of thinking use the idioms that its authors expected you
to use.
But if you're creating your own framework and/or a piece of the system that
does not directly depend on MFC, then don't catch by pointer just because MFC
does it that way. When you're not in Rome, you don't
necessarily do as the Romans. In this case, you should not.
Libraries like MFC predated the standardization of exception handling in the
C++ language, and some of these libraries use a backwards-compatible form of
exception handling that requires (or at least encourages) you to catch by
pointer.
The problem with catching by pointer is that it's not clear who (if anyone) is
responsible for deleting the pointed-to object. For example, consider the
following:
MyException x;
void f()
{
MyException y;
try {
switch ((rand() >> 8) % 3) { // the ">> 8" (typically) improves the period of the lowest 2 bits
case 0: throw new MyException;
case 1: throw &x;
case 2: throw &y;
}
}
catch (MyException* p) {
... ← should we delete p here or not???!?
}
}
There are three basic problems here:
- It might be tough to decide whether to delete p within the
catch clause. For example, if object x is inaccessible to the
scope of the catch clause, such as when it's buried in the private
part of some class or is static within some other compilation unit, it
might be tough to figure out what to do.
- If you solve the first problem by consistently using new in the
throw (and therefore consistently using delete in the
catch), then exceptions always use the heap which can cause problems
when the exception was thrown because the system was running low on
memory.
- If you solve the first problem by consistently not using
new in the throw (and therefore consistently not using
delete in the catch), then you probably won't be able to
allocate your exception objects as locals (since then they might get
destructed too early), in which case you'll have to worry about thread-safety,
locks, semaphores, etc. (static objects are not intrinsically
thread-safe).
This isn't to say it's not possible to work through these issues. The point
is simply this: if you catch by reference rather than by pointer, life is
easier. Why make life hard when you don't have to?
The moral: avoid throwing pointer expressions, and avoid catching by pointer,
unless you're using an existing library that "wants" you to do so.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.9] What does throw; (without an exception object after the throw keyword) mean? Where would I use it?
You might see code that looks something like this:
class MyException {
public:
...
void addInfo(const std::string& info);
...
};
void f()
{
try {
...
}
catch (MyException& e) {
e.addInfo("f() failed");
throw;
}
}
In this example, the statement throw; means "re-throw the current
exception." Here, a function caught an exception (by non-const reference),
modified the exception (by adding information to it), and then re-threw the
exception. This idiom can be used to implement a simple form of stack-trace,
by adding appropriate catch clauses in the important functions of your
program.
Another re-throwing idiom is the "exception dispatcher":
void handleException()
{
try {
throw;
}
catch (MyException& e) {
...code to handle MyException...
}
catch (YourException& e) {
...code to handle YourException...
}
}
void f()
{
try {
...something that might throw...
}
catch (...) {
handleException();
}
}
This idiom allows a single function (handleException()) to be re-used
to handle exceptions in a number of other functions.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.10] How do I throw polymorphically?
Sometimes people write code like:
class MyExceptionBase { };
class MyExceptionDerived : public MyExceptionBase { };
void f(MyExceptionBase& e)
{
// ...
throw e;
}
void g()
{
MyExceptionDerived e;
try {
f(e);
}
catch (MyExceptionDerived& e) {
...code to handle MyExceptionDerived...
}
catch (...) {
...code to handle other exceptions...
}
}
If you try this, you might be surprised at run-time when your catch
(...) clause is entered, and not your catch (MyExceptionDerived&)
clause. This happens because you didn't throw polymorphically. In function
f(), the statement throw e; throws an object with the same
type as the static type of the expression e. In other words,
it throws an instance of MyExceptionBase. The throw statement
behaves as-if the thrown object is copied, as opposed to
making a "virtual copy".
Fortunately it's relatively easy to correct:
class MyExceptionBase {
public:
virtual void raise();
};
void MyExceptionBase::raise()
{ throw *this; }
class MyExceptionDerived : public MyExceptionBase {
public:
virtual void raise();
};
void MyExceptionDerived::raise()
{ throw *this; }
void f(MyExceptionBase& e)
{
// ...
e.raise();
}
void g()
{
MyExceptionDerived e;
try {
f(e);
}
catch (MyExceptionDerived& e) {
...code to handle MyExceptionDerived...
}
catch (...) {
...code to handle other exceptions...
}
}
Note that the throw statement has been moved into a virtual function.
The statement e.raise() will exhibit polymorphic behavior, since
raise() is declared virtual and e was passed by
reference. As before, the thrown object will be of the static type of
the argument in the throw statement, but within
MyExceptionDerived::raise(), that static type is
MyExceptionDerived, not MyExceptionBase.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.11] When I throw this object, how many times will it be copied?
Depends. Might be "zero."
Objects that are thrown must have a publicly accessible copy-constructor. The
compiler is allowed to generate code that copies the thrown object any number
of times, including zero. However even if the compiler never actually copies
the thrown object, it must make sure the exception class's copy constructor
exists and is accessible.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.12] Exception handling seems to make my life more difficult; clearly I'm not the problem, am I??
Absolutely you might be the problem!
The C++ exception handling mechanism can be powerful and useful, but if you
use it with the wrong mindset, the result can be a mess. If you're getting
bad results, for instance, if your code seems unnecessarily convoluted or
overly cluttered with try blocks, you might be suffering from a "wrong
mindset." This FAQ gives you a list of some of those wrong mindsets.
Warning: do not be simplistic about these "wrong mindsets." They are
guidelines and ways of thinking, not hard and fast rules. Sometimes you will
do the exact opposite of what they recommend do not write me about
some situation that is an exception (no pun intended) to one or more of them
I guarantee that there are exceptions. That's not the point.
Here are some "wrong exception-handling mindsets" in no apparent order:
- The return-codes mindset: This causes programmers to clutter their
code with gobs of try blocks. Basically they think of a throw
as a glorified return code, and a try/catch as a glorified "if
the return code indicates an error" test, and they put one of these
try blocks around just about every function that can
throw.
- The Java mindset: In Java, non-memory resources are reclaimed via
explicit try/finally blocks. When this mindset is used in
C++, it results in a large number of unnecessary try blocks, which,
compared with RAII, clutters the code and makes the logic harder to follow.
Essentially the code swaps back and forth between the "good path" and the "bad
path" (the latter meaning the path taken during an exception). With RAII, the
code is mostly optimistic it's all the "good path," and the cleanup code
is buried in destructors of the resource-owning objects. This also helps
reduce the cost of code reviews and unit-testing, since these "resource-owning
objects" can be validated in isolation (with explicit
try/catch blocks, each copy must be unit-tested and inspected
individually; they cannot be handled as a group).
- Organizing the exception classes around the physical thrower rather
than the logical reason for the throw: For example, in a banking app,
suppose any of five subsystems might throw an exception when the customer has
insufficient funds. The right approach is to throw an exception representing
the reason for the throw, e.g., an "insufficient funds exception"; the
wrong mindset is for each subsystem to throw a subsystem-specific exception.
For example, the Foo subsystem might throw objects of class
FooException, the Bar subsystem might throw objects of class
BarException, etc. This often leads to extra
try/catch blocks, e.g., to catch a FooException,
repackage it into a BarException, then throw the latter. In general,
exception classes should represent the problem, not the chunk of code that
noticed the problem.
- Using the bits / data within an exception object to differentiate
different categories of errors: Suppose the Foo subsystem in our
banking app throws exceptions for bad account numbers, for attempting to
liquidate an illiquid asset, and for insufficient funds. When these three
logically distinct kinds of errors are represented by the same exception
class, the catchers need to say if to figure out what the problem
really was. If your code wants to handle only bad account numbers, you need
to catch the master exception class, then use if to determine
whether it is one you really want to handle, and if not, to rethrow it. In
general, the preferred approach is for the error condition's logical category
to get encoded into the type of the exception object, not into the
data of the exception object.
- Designing exception classes on a subsystem by subsystem basis: In
the bad old days, the specific meaning of any given return-code was local to a
given function or API. Just because one function uses the return-code of 3 to
mean "success," it was still perfectly acceptable for another function to use
3 to mean something entirely different, e.g., "failed due to out of memory."
Consistency has always been preferred, but often that didn't happen
because it didn't need to happen. People coming with that mentality
often treat C++ exception-handling the same way: they assume exception classes
can be localized to a subsystem. That causes no end of grief, e.g., lots of
extra try blocks to catch then throw a repackaged
variant of the same exception. In large systems, exception hierarchies
must be designed with a system-wide mindset. Exception classes cross
subsystem boundaries they are part of the intellectual glue that holds the
architecture together.
- Use of raw (as opposed to smart) pointers: This is actually just a
special case of non-RAII coding, but I'm calling it out because it is so
common. The result of using raw pointers is, as above, lots of extra
try/catch blocks whose only purpose in life is to
delete an object then re-throw the exception.
- Confusing logical errors with runtime situations: For example,
suppose you have a function f(Foo* p) that must never be called with
the NULL pointer. However you discover that somebody somewhere is sometimes
passing a NULL pointer anyway. There are two possibilities: either they are
passing NULL because they got bad data from an external user (for example, the
user forgot to fill in a field and that ultimately resulted in a NULL pointer)
or they just plain made a mistake in their own code. In the former case, you
should throw an exception since it is a runtime situation (i.e., something you
can't detect by a careful code-review; it is not a bug). In the latter case,
you should definitely fix the bug in the caller's code. You can still add
some code to write a message in the log-file if it ever happens again, and you
can even throw an exception if it ever happens again, but you must not merely
change the code within f(Foo* p); you must, must, MUST
fix the code in the caller(s) of f(Foo* p).
There are other "wrong exception-handling mindsets," but hopefully those will
help you out. And remember: don't take those as hard and fast rules. They
are guidelines, and there are exceptions to each.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[17.13] I have too many try blocks; what can I do about it?
You might have the mindset of return codes even though you are using
the syntax of try/catch/throw. For instance,
you might put a try block around just about every call:
void myCode()
{
try {
foo();
}
catch (FooException& e) {
...
}
try {
bar();
}
catch (BarException& e) {
...
}
try {
baz();
}
catch (BazException& e) {
...
}
}
Although this uses the try/catch/throw syntax, the
overall structure is very similar to the way things are done with return
codes, and the consequent software development/test/maintenance costs are
basically the same as they were for return codes. In other words, this
approach doesn't buy you much over using return codes. In general, it is bad
form.
One way out is to ask yourself this question for each try block: "Why am I
using a try block here?" There are several possible answers:
- Your answer might be, "So I can actually handle the exception. My catch
clause deals with the error and continues execution without throwing any
additional exceptions. My caller never knows that the exception occurred. My
catch clause does not throw any exceptions and it does not return any
error-codes." In that case, you leave the try block as-is it is probably
good.
- Your answer might be, "So I can have a catch clause that does blah
blah blah, after which I will rethrow the exception." In this case,
consider changing the try block into an object whose destructor does blah
blah blah. For instance, if you have a try block whose catch clause
closes a file then rethrows the exception, consider replacing the whole thing
with a File object whose destructor closes the file. This is commonly
called RAII.
- Your answer might be, "So I can repackage the exception: I catch a
XyzException, extract the details, then throw a PqrException."
When that happens, consider a better hierarchy of exception objects that
doesn't require this catch/repackage/rethrow idea. This often involves
broadening the meaning of XyzException, though obviously you shouldn't
go too far.
- There are other answers as well, but the above are some common ones that
I've seen.
Main point is to ask "Why?". If you discover the reason you're doing
it, you might find that there are better ways to achieve your goal.
Having said all this, there are, unfortunately, some people who have the
return-code-mindset burned so deeply into their psyche that they just can't
seem to see any alternatives. If that is you, there is still hope:
get a mentor. If you see it done right, you'll
probably get it. Style is sometimes caught, not just taught.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised Mar 1, 2006