C++ Programming Guide | ![]() ![]() ![]() ![]() ![]() |
Exception Handling
This chapter explains exception handling as it is currently implemented in the Sun C++ compiler and discusses the requirements of the C++ International Standard.
For additional information on exception handling, see The C++ Programming Language, Third Edition, by Bjarne Stroustrup (Addison-Wesley, 1997).
5.1 Understanding Exception Handling
Exceptions are anomalies that occur during the normal flow of a program and prevent it from continuing. These anomalies--user, logic, or system errors--can be detected by a function. If the detecting function cannot deal with the anomaly, it "throws" an exception. A function that "handles" that kind of exception catches it.
In C++, when an exception is thrown, it cannot be ignored--there must be some kind of notification or termination of the program. If no user-provided exception handler is present, the compiler provides a default mechanism to terminate the program.
Exception handling is expensive compared to ordinary program flow controls, such as loops or if-statements. It is therefore better not to use the exception mechanism to deal with ordinary situations, but to reserve it for situations that are truly unusual.
Exceptions are particularly helpful in dealing with situations that cannot be handled locally. Instead of propagating error status throughout the program, you can transfer control directly to the point where the error can be handled.
For example, a function might have the job of opening a file and initializing some associated data. If the file cannot be opened or is corrupted, the function cannot do its job. However, that function might not have enough information to handle the problem. The function can throw an exception object that describes the problem, transferring control to an earlier point in the program. The exception handler might automatically try a backup file, query the user for another file to try, or shut down the program gracefully. Without exception handlers, status and data would have to be passed down and up the function call hierarchy, with status checks after every function call. With exception handlers, the flow of control is not obscured by error checking. If a function returns, the caller can be certain that it succeeded.
Exception handlers have disadvantages. If a function does not return because it, or some other function it called, threw an exception, data might be left in an inconsistent state. You need to know when an exception might be thrown, and whether the exception might have a bad effect on the program state.
For information about using exceptions in a multithreaded environment, see Section 9.2 Using Exceptions in a Multithreaded Program.
5.2 Using Exception Handling Keywords
There are three keywords for exception handling in C++:
try
catch
throw
5.2.1
try
A
try
block is a group of C++ statements, enclosed in braces{
}
, that might cause an exception. This grouping restricts exception handlers to exceptions generated within thetry
block. Eachtry
block has one or more associatedcatch
blocks.5.2.2
catch
A
catch
block is a group of C++ statements that are used to handle a specific thrown exception. One or morecatch
blocks, or handlers, should be placed after eachtry
block. Acatch
block is specified by:
- The keyword
catch
- A
catch
parameter, enclosed in parentheses()
, which corresponds to a specific type of exception that may be thrown by thetry
block- A group of statements, enclosed in braces
{
},
whose purpose is to handle the exception5.2.3
throw
The
throw
statement is used to throw an exception and its value to a subsequent exception handler. A regular throw consists of the keywordthrow
and an expression. The result type of the expression determines whichcatch
block receives control. Within acatch
block, the current exception and value may be re-thrown simply by specifying thethrow
keyword alone (with no expression).In the following example, the function call in the
try
block passes control tof()
, which throws an exception of typeOverflow
. This exception is handled by thecatch
block, which handles typeOverflow
exceptions.
5.3 Implementing Exception Handlers
To implement an exception handler, perform these basic tasks:
- When a function is called by many other functions, code it so that an exception is thrown whenever an error is detected. The
throw
expression throws an object. This object is used to identify the types of exceptions and to pass specific information about the exception that has been thrown.- Use the
try
statement in a client program to anticipate exceptions. Enclose function calls that might produce an exception in atry
block.- Code one or more
catch
blocks immediately after thetry
block. Eachcatch
block identifies what type or class of objects it is capable of catching. When an object is thrown by the exception, the following actions occur:
- If the object thrown by the exception matches the type of the
catch
expression, control passes to thatcatch
block.- If the object thrown by the exception does not match the first
catch
block, subsequentcatch
blocks are searched for a matching type (see Section 5.8 "Matching Exceptions With Handlers"").- If there is no catch block at the current scope matching the thrown exception, the current scope is exited, and all automatic (local nonstatic) objects defined in that scope are destroyed. The surrounding scope (which might be function scope) is checked for a matching handler. This process is continued until a scope is found that has a matching catch block. If one is found before exiting function
main()
, that catch block is entered.- If there is no match in any of the
catch
blocks, the program is normally terminated with a call to the predefined functionterminate()
. By default,terminate()
callsabort()
, which destroys all remaining objects and exits from the program. This default behavior can be changed by calling theset_terminate()
function.5.3.1 Synchronous Exception Handling
Exception handling is designed to support only synchronous exceptions, such as array range checks. The term synchronous exception means that exceptions can only be originated from
throw
expressions.The C++ standard supports synchronous exception handling with a termination model. Termination means that once an exception is thrown, control never returns to the throw point.
5.3.2 Asynchronous Exception Handling
Exception handling is not designed to directly handle asynchronous exceptions such as keyboard interrupts. However, you can make exception handling work in the presence of asynchronous events if you are careful. For instance, to make exception handling work with signals, you can write a signal handler that sets a global variable, and create another routine that polls the value of that variable at regular intervals and throws an exception when the value changes. You cannot throw an exception from a signal handler.
5.4 Managing Flow of Control
In C++, exception handlers do not correct the exception and then return to the point at which the exception occurred. Instead, when an exception is generated, control is passed out of the block that threw the exception, out of the
try
block that anticipated the exception, and into thecatch
block whose exception declaration matches the exception thrown.The
catch
block handles the exception. It might rethrow the same exception, throw another exception, jump to a label, return from the function, or end normally. If acatch
block ends normally, without athrow
, the flow of control passes over all othercatch
blocks associated with thetry
block.Whenever an exception is thrown and caught, and control is returned outside of the function that threw the exception, stack unwinding takes place. During stack unwinding, any automatic objects that were created within the scope of the block that was exited are safely destroyed via calls to their destructors.
If a
try
block ends without an exception, all associatedcatch
blocks are ignored.
Note An exception handler cannot return control to the source of the error by using thereturn
statement. Areturn
statement issued in this context returns from the function containing thecatch
block.
5.4.1 Branching Into and Out of
try
Blocks and HandlersBranching out of a
try
block or a handler is allowed. Branching into acatch
block is not allowed, however, because that is equivalent to jumping past an initiation of the exception.5.4.2 Nesting of Exceptions
Nesting of exceptions, that is, throwing an exception while another remains unhandled, is allowed only in restricted circumstances. From the point when an exception is thrown to the point when the matching
catch
clause is entered, the exception is unhandled. Functions that are called along the way, such as destructors of automatic objects being destroyed, may throw new exceptions, as long as the exception does not escape the function. If a function exits via an exception while another exception remains unhandled, theterminate()
function is called immediately.Once an exception handler has been entered, the exception is considered handled, and exceptions may be thrown again.
You can determine whether any exception has been thrown and is currently unhandled. See Section 5.7 Calling the uncaught_exception() Function.
5.4.3 Specifying Exceptions to Be Thrown
A function declaration can include an exception specification, a list of exceptions that a function may throw, directly or indirectly.
The two following declarations indicate to the caller that the function
f1
generates only exceptions that can be caught by a handler of typeX
, and that the functionf2
generates only exceptions that can be caught by handlers of typeW
,Y
, orZ
:
void f1(int) throw(X);void f2(int) throw(W,Y,Z);A variation on the previous example is:
void f3(int) throw(); // empty parenthesesThis declaration guarantees that no exception is generated by the function
f3
. If a function exits through any exception that is not allowed by an exception specification, it results in a call to the predefined functionunexpected()
. By default,unexpected()
callsterminate()
which by default exits the program. You can change this default behavior by calling theset_unexpected()
function. See Section 5.6.2 set_unexpected().The check for unexpected exceptions is done at program execution time, not at compile time. Even if it appears that a disallowed exception might be thrown, there is no error unless the disallowed exception is actually thrown at runtime.
The compiler can, however, eliminate unnecessary checking in some simple cases. For instance, no checking for
f
is generated in the following example.
void foo(int) throw(x);void f(int) throw(x);{ foo(13);}The absence of an exception specification allows any exception to be thrown.
5.5 Specifying Runtime Errors
There are five runtime error messages associated with exceptions:
- No handler for the exception
- Unexpected exception thrown
- An exception can only be re-thrown in a handler
- During stack unwinding, a destructor must handle its own exception
- Out of memory
When errors are detected at runtime, the error message displays the type of the current exception and one of the five error messages. By default, the predefined function
terminate()
is called, which then callsabort()
.The compiler uses the information provided in the exception specification to optimize code production. For example, table entries for functions that do not throw exceptions are suppressed, and runtime checking for exception specifications of functions is eliminated wherever possible. Thus, declaring functions with correct exception specifications can lead to better code generation.
5.6 Modifying the
terminate()
andunexpected()
FunctionsThe following sections describe how to modify the behavior of the
terminate()
andunexpected()
functions usingset_terminate()
andset_unexpected()
. For information about using these functions in a multithreaded environment, see Section 9.2 Using Exceptions in a Multithreaded Program.5.6.1
set_terminate()
You can modify the default behavior of
terminate()
by calling the functionset_terminate()
, as shown in the following example.
// declarations are in standard header <exception>namespace std {typedef void (*terminate_handler)();terminate_handler set_terminate(terminate_handler f) throw();void terminate();}The
terminate()
function is called in any of the following circumstances:
- The exception handling mechanism calls a user function (including destructors for automatic objects) that exits through an uncaught exception while another exception remains uncaught.
- The exception handling mechanism cannot find a handler for a thrown exception.
- The construction or destruction of a nonlocal object with static storage duration exits using an exception.
- Execution of a function registered with
atexit()
exits using an exception.- A
throw
expression with no operand attempts to rethrow an exception and no exception is being handled.- The
unexpected()
function throws an exception that is not allowed by the previously violated exception specification, andstd::bad_exception
is not included in that exception specification.- The default version of
unexpected()
is called.The
terminate()
function calls the function passed as an argument toset_terminate()
. Such a function takes no parameters, returns no value, and must terminate the program (or the current thread). The function passed in the most recent call toset_terminate()
is called. The previous function passed as an argument toset_terminate()
is the return value, so you can implement a stack strategy for usingterminate()
. The default function forterminate()
callsabort()
for the main thread andthr_exit()
for other threads. Note thatthr_exit()
does not unwind the stack or call C++ destructors for automatic objects.
Note A replacement forterminate()
must not return to its caller.
5.6.2
set_unexpected()
You can modify the default behavior of
unexpected()
by calling the functionset_unexpected()
, as shown in the following example.
The
unexpected()
function is called when a function attempts to exit through an exception not listed in its exception specification. The default version ofunexpected()
callsterminate()
.A replacement version of
unexpected()
might throw an exception permitted by the violated exception specification. If it does so, exception handling continues as though the original function had really thrown the replacement exception. If the replacement forunexpected()
throws any other exception, that exception is replaced by the standard exceptionstd::bad_exception
. If the original function's exception specification does not allowstd::bad_exception
, functionterminate()
is called immediately. Otherwise, exception handling continues as though the original function had really thrownstd::bad_exception
.
unexpected()
calls the function passed as an argument toset_unexpected()
. Such a function takes no parameters, returns no value, and must not return to its caller. The function passed in the most recent call toset_unexpected()
is called. The previous function passed as an argument toset_unexpected()
is the return value, so you can implement a stack strategy for usingunexpected()
.
Note A replacement forunexpected()
must not return to its caller.
5.7 Calling the
uncaught_exception()
FunctionAn uncaught, or active, exception is an exception that has been thrown, but not yet accepted by a handler. The function
uncaught_exception()
returnstrue
if there is an uncaught exception, andfalse
otherwise.The
uncaught_exception()
function is most useful for preventing program termination due to a function that exits with an uncaught exception while another exception is still active. This situation most commonly occurs when a destructor called during stack unwinding throws an exception. To prevent this situation, make sureuncaught_exception()
returnsfalse
before throwing an exception within a destructor. (Another way to prevent such termination is to design your program so that destructors do not need to throw exceptions.)5.8 Matching Exceptions With Handlers
A handler type
T
matches athrow
typeE
if any one of the following is true:
T
is the same asE.
T
isconst
orvolatile
ofE.
E
isconst
orvolatile
ofT.
T
is ref ofE
orE
is ref ofT.
T
is apublic
base class ofE.
T
andE
are both pointer types, andE
can be converted toT
by a standard pointer conversion.Throwing exceptions of reference or pointer types can result in a dangling pointer if the object pointed or referred to is destroyed before exception handling is complete. When an object is thrown, a copy of the object is always made through the copy constructor, and the copy is passed to the
catch
block. It is therefore safe to throw a local or temporary object.While handlers of type
(X)
and(X&)
both match an exception of typeX
, the semantics are different. Using a handler with type (X
) invokes the object's copy constructor (again). If the thrown object is of a type derived from the handler type, the object is truncated. Catching a class object by reference therefore usually executes faster.Handlers for a
try
block are tried in the order of their appearance. Handlers for a derived class (or a pointer to a reference to a derived class) must precede handlers for the base class to ensure that the handler for the derived class can be invoked.5.9 Checking Access Control in Exceptions
The compiler performs the following check on access control for exceptions:
- The formal argument of a
catch
clause obeys the same rules as an argument of the function in which thecatch
clause occurs.- An object can be thrown if it can be copied and destroyed in the context of the function in which the
throw
occurs.Currently, access controls do not affect matching.
No other access is checked at runtime except for the matching rule described in Section 5.8 "Matching Exceptions With Handlers"".
5.10 Enclosing Functions in
try
BlocksIf the constructor for a base class or member of a class
T
exits via an exception, there would ordinarily be no way for theT
constructor to detect or handle the exception. The exception would be thrown before the body of theT
constructor is entered, and thus before anytry
block inT
could be entered.A new feature in C++ is the ability to enclose an entire function in a
try
block. For ordinary functions, the effect is no different from placing the body of the function in atry
block. But for a constructor, thetry
block traps any exceptions that escape from initializers of base classes and members of the constructor's class. When the entire function is enclosed in atry
block, the block is called a function try block.In the following example, any exception thrown from the constructor of base class
B
or membere
is caught before the body of theT
constructor is entered, and is handled by the matchingcatch
block.You cannot use a
return
statement in the handler of a functiontry
block, because thecatch
block is outside the function. You can only throw an exception or terminate the program by callingexit()
orterminate()
.
5.11 Disabling Exceptions
If you know that exceptions are not used in a program, you can use the compiler option
-features=no%except
to suppress generation of code that supports exception handling. The use of the option results in slightly smaller code size and faster code execution. However, when files compiled with exceptions disabled are linked to files using exceptions, some local objects in the files compiled with exceptions disabled are not destroyed when exceptions occur. By default, the compiler generates code to support exception handling. Unless the time and space overhead is important, it is usually better to leave exceptions enabled.5.12 Using Runtime Functions and Predefined Exceptions
The standard header
<exception>
provides the classes and exception-related functions specified in the C++ standard. You can access this header only when compiling in standard mode (compiler default mode, or with option-compat=5
). The following excerpt shows the<exception>
header file declarations.
The standard class
exception
is the base class for all exceptions thrown by selected language constructs or by the C++ standard library. An object of typeexception
can be constructed, copied, and destroyed without generating an exception. The virtual member functionwhat()
returns a character string that describes the exception.For compatibility with exceptions as used in C++ release 4.2, the header
<exception.h>
is also provided for use in standard mode. This header allows for a transition to standard C++ code and contains declarations that are not part of standard C++. Update your code to follow the C++ standard (using<exception>
instead of<exception.h>
) as development schedules permit.
In compatibility mode (
--compat[=4]
), header<exception>
is not available, and header<exception.h>
refers to the same header provided with C++ release 4.2. It is not reproduced here.5.13 Mixing Exceptions With Signals and
Setjmp
/Longjmp
You can use
setjmp
/longjmp
in a program where exceptions can occur, as long as they don't interact.All the rules for using exceptions and
setjmp
/longjmp
separately apply. In addition, alongjmp
from point A to point B is valid only if an exception thrown at A and caught at B would have the same effect. In particular, you must notlongjmp
into or out of a try-block or catch-block (directly or indirectly), orlongjmp
past the initialization or non-trivial destruction of auto or temporary variables.You cannot throw an exception from a signal handler.
5.14 Building Shared Libraries That Have Exceptions
When shared libraries are opened with
dlopen,
you must useRTLD_GLOBAL
for exceptions to work.
Note When building shared libraries that contain exceptions, do not pass the option-Bsymbolic
told
. Exceptions that should be caught might be missed.
Sun Microsystems, Inc. Copyright information. All rights reserved. Feedback |
Library | Contents | Previous | Next | Index |