Overview
Teaching: 20 min
Exercises: 0 minQuestions
How can programs deal with exceptional conditions?
Objectives
Learn how to protect code from exceptional conditions.
Learn the syntax of a try-catch block.
Learn about how exception handlers are invoked.
Learn about standard exceptions.
Exceptions provide a way to deal with exceptional conditions in your program. An exception is a value used to represent something unusual that has happened in the program. They are usually created in response to a runtime error condition. Exceptions are dealt with by being caught and execution of the program is transferred to a special function called a handler.
To catch an exception, a section of code is enclosed in a special construct called a try
block.
When an exceptional circumstance arises within that block, an exception is thrown that transfers the control to the exception handler.
If no exception is thrown, the code continues normally and all handlers are ignored.
An exception is thrown by using the throw
keyword from somewhere inside the try
block, or from calling other function in the block.
Handlers are declared with the keyword catch
, and are placed immediately after the closing brace of the try
block.
The following example shows a simple try
block in action.
// exceptions
#include <iostream>
using namespace std;
int main () {
try
{
throw 20;
}
catch (int e)
{
cout << "An exception occurred. Exception Nr. " << e << endl;
}
return 0;
}
The code enclosed in the try
block is executed and if an exception occurs, control is transferred to the catch
block. In this example
the code simply throws an exception using the statement:
throw 20;
A throw
expression accepts one parameter which is passed as an argument to the exception handler.
The syntax for catch
is similar to a regular function with one parameter. The type of this parameter is very important, since the type of the
argument passed by the throw
expression is checked against it, and only if the types match, will the exception be caught by that handler.
Multiple handlers (i.e., catch
expressions) can be chained, each one with a different parameter type. Only the handler whose argument type
matches the type of the exception specified in the throw
statement is executed.
If an ellipsis ...
is used as the parameter of catch
, that handler will catch any exception no matter what the type of the exception thrown.
This can be used as a default handler that catches all exceptions not caught by other handlers.
The following shows an example of chaining exception handlers:
try {
// code here
}
catch (int param) { cout << "int exception"; }
catch (char param) { cout << "char exception"; }
catch (...) { cout << "default exception"; }
In this case, the last handler would catch any exception thrown of a type that is neither int
nor char
.
After an exception has been handled the program, execution resumes after the entire try-catch
block, not after the throw
statement!.
It is also possible to nest a try-catch
block within a try
block. If the nested catch block wishes to forward the exception
to the next level, it can do so with the statement throw;
.
For example:
try {
try {
// code here
}
catch (int n) {
throw;
}
}
catch (...) {
cout << "Exception occurred";
}
The C++ Standard library provides a base class specifically designed to declare objects to be thrown as exceptions.
It is called std::exception
and is defined in the <exception>
header. This class has a virtual member function called
what
that returns a null-terminated character sequence (of type char *
) and that can be overwritten in derived classes
to contain some sort of description of the exception.
// using standard exceptions
#include <iostream>
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main() {
try
{
throw myex;
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
In this example, the handler catches exception objects by reference (notice the ampersand &
after the type). This allows the
handler to also catch classes derived from exception
, like our myex
object of type myexception
.
All exceptions thrown by components of the C++ Standard library throw exceptions derived from this exception
class. These are:
exception | description |
---|---|
bad_alloc | thrown by new on allocation failure |
bad_cast | thrown by dynamic_cast when it fails in a dynamic cast |
bad_exception | thrown by certain dynamic exception specifiers |
bad_typeid | thrown by typeid |
bad_function_call | thrown by empty function objects |
bad_weak_ptr | thrown by shared_ptr when passed a bad weak_ptr |
Header <exception>
also defines two generic exception types that are also derived from exception
. These can be inherited by
custom exceptions to report errors:
exception | description |
---|---|
logic_error | error related to the internal logic of the program |
runtime_error | error detected during runtime |
A typical example where exception handling should always be used is when allocating memory:
// bad_alloc standard exception
#include <iostream>
#include <exception>
using namespace std;
int main () {
try
{
int* myarray= new int[1000];
}
catch (exception& e)
{
cout << "Standard exception: " << e.what() << endl;
}
return 0;
}
The exception that may be caught by the exception handler in this example is a bad_alloc
. Because bad_alloc
is derived from the standard base class exception, it can be caught by using a single handler.
Key Points
Exceptional conditions occur within any program.
Handling exceptions is important to ensuring program correctness.
Exception handlers are just like regular functions.
Operations in the standard library throw exceptions.