Exceptions


Exceptions provide a way to react to exceptional circumstances (like runtime errors) in programs by transferring control to special code blocks called handlers.

To catch exceptions, a portion of code is placed under exception inspection. This is done by enclosing that portion of code in a try-block. When an exceptional circumstance arises within that block, an exception is thrown that transfers the control to the exception handler block. If no exception is thrown, the code continues normally and all handlers are ignored.

An exception is thrown by using the throw keyword from inside the try block. Exception handlers are declared with the keyword catch, which must be placed immediately after the try block:

#include <iostream>
#include <stdexcept>
#include <cstdlib>

using std::cin;
using std::cout;
using std::endl;
using std::invalid_argument;

int main(int argc, char* argv[])
{
    int value;

    try
    {
        if (argc == 1)
            throw invalid_argument("No command line arguments specified");

        cout << argv[1] << endl;
    }
    catch (const invalid_argument& e)
    {
        cout << "Error: " << e.what() << endl;
    }

    // Following code...
    
    ...
    
    return 0;
}

The code under exception inspection is enclosed in a try block. In this example this code simply throws an exception if there are no command-line arguments specified when the program is run. A throw expression accepts an expression (in this case, an unnamed object of the class invalid_argument), which is passed as an argument to the exception handler.

invalid_argument is a standard library exception class defined in the header file stdexcept. The constructor for the class takes a string argument, which can later be accessed by calling the what() member function of the exception object passed to the exception handler.

The exception handler is declared with the catch keyword immediately after the closing brace of the try block. 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 in the case that they match is the exception caught by that handler.

In the example above, if no command line arguments are specified, the exception is thrown and program control is transferred to the catch handler block that matches the data type invalid_argument. An error message is printed and then the program continues execution after the try-catch structure, not after the throw statement that threw the exception.

If at least one argument is specified after the program name when the program is run, then no exception is thrown and the argument's value is printed. The catch block is skipped entirely and the program continues execution after the try-catch structure.

Multiple Handlers

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 an exception of any data type. This can be used as a default handler that catches all exceptions not caught by other handlers:

try
{
    // Code that could throw an exception
}
catch (const overflow_error& e)
{
    cout << "Invalid argument\n" <<;
}
catch (const underflow_error& e)
{
    cout << "Out of range\n" <<;
}
catch (...)
{
    cout << "Default exception\n";
}

In this case, the last handler would catch any exception thrown of a type that is neither overflow_error nor underflow_error.

It is also possible to nest try-catch structures within an outer try block. In that case, we have the option of an inner catch block simply passing an exception to its enclosing try-catch structure. This is done using the expression throw; with no arguments. For example:

try
{
    try
    {
        // Code that could throw an exception
    }
    catch (const out_of_range& e)
    {
        throw;
    }
}
catch (...)
{
    cout << "Exception occurred\n";
}

Responding to Caught Exceptions

A handler block may respond to a caught exception in a variety of ways, depending on the needs of the application. Some possible responses include:

Standard Exceptions

Practically any C++ data type can be used in a throw expression, but objects of class types are probably the most commonly used. The standard library defines a number of exception classes in the header file <stdexcept>, some of which are briefly described below. This list is not exhaustive.

Exception Description
exception Base class for standard exceptions. All of the other exception classes listed are directly or indirectly derived from this class.
logic_error Derived class of exception. Serves as the base class for logic error exceptions, including domain_error, invalid_argument, length_error, and out_of_range.
runtime_error Derived class of exception. Serves as the base class for runtime error exceptions, including overflow_error and underflow_error.
bad_alloc Derived class of exception. Thrown by new and new[] on allocation failure
bad_cast Derived class of exception. Thrown by dynamic_cast when it fails in a dynamic cast
domain_error Derived class of logic_error. Can be thrown to report domain errors, that is, situations where the inputs are outside of the domain on which an operation is defined. The standard library components do not throw this exception.
invalid_argument Derived class of logic_error. Can be thrown to report an invalid argument. Some components of the standard library throw exceptions of this type.
length_error Derived class of logic_error. Can be thrown to report a length error. Some components of the standard library such as the reserve() member functions of vector and string throw exceptions of this type.
out_of_range Derived class of logic_error. Can be thrown to report an argument that is out-of-range. Some components of the standard library such as the at() member functions of vector, string, deque, and array throw exceptions of this type.
overflow_error Derived class of runtime_error. Can be thrown to report an arithmetic overflow error. Some components of the standard library throw exceptions of this type.
underflow_error Derived class of runtime_error. Can be thrown to report an arithmetic underflow error. The standard library components do not throw this exception.