Skip to content
Theory · Exceptions · lesson 4 of 9

Standard exceptions

So far we have thrown our own alarm balls. The standard library throws exceptions too, and its balls already carry signs with the error type. So you can prepare a net for them in advance.

For example, the at method of a vector checks the index and, if there is no such element, throws a std::out_of_range exception:

C++
#include <iostream>
#include <stdexcept>
#include <vector>

int main() {
    std::vector<int> v{1, 2, 3};
    try {
        std::cout << v.at(5) << '\n';
    } catch (const std::out_of_range&) {
        std::cout << "no element 5\n";  // no element 5
    }
}

The program prints no element 5: the vector has three elements, and v.at(5) throws an exception that the handler catches.

In the containers theme, at stopped the program for a missing key. The program ends the same way on any exception that nobody catches.

The std::exception hierarchy

The standard exception classes derive from std::exception in the <exception> header. std::exception has a virtual method what(): it returns the text describing the error. The type of that text is const char*, a pointer to the first character, and std::cout prints it like an ordinary string.

The classes below are declared in the <stdexcept> header:

  • std::logic_error—an error in the program's logic: a condition the caller could have checked in advance is broken;
  • std::invalid_argument (derived from std::logic_error)—an invalid argument;
  • std::out_of_range (also derived from std::logic_error)—an index or a key outside the valid bounds;
  • std::runtime_error—an error that can be detected only while the program runs.

The constructor of each of these classes takes a message text, and what() returns exactly that text:

C++
int parse_digit(char c) {
    if (c < '0' || c > '9') {
        throw std::invalid_argument("not a digit");
    }
    return c - '0';
}

A handler for std::exception catches any standard exception, and the virtual what() is called on the object that was thrown:

C++
try {
    std::cout << parse_digit('x') << '\n';
} catch (const std::exception& e) {
    std::cout << e.what() << '\n';  // not a digit
}

Your own exception class is also declared as a derived class of one of these, usually std::runtime_error. Then a handler for std::exception catches it too, and what() returns the text passed to the std::runtime_error constructor.

Throw values, catch references

A throw expression creates a separate object from its operand—the exception object. It lives at least until the handler that caught it finishes. So you throw the object itself, not a pointer to it: a pointer to a local variable dangles after stack unwinding.

An exception is caught by const reference: const std::exception& e denotes the exception object itself, with no copy. It can also be caught by value, catch (std::exception e), but then e is a new copy of the type in parentheses. For a derived class this is object slicing: the copy is a plain std::exception. Calling what() on it runs std::exception::what, not the thrown class's version.

Common mistake

The exception is created with new:

C++
try {
    throw new std::runtime_error("disk full");  // ✗
} catch (const std::exception& e) {
    std::cout << e.what() << '\n';
}

The compiler does not warn, but the thrown value is a std::runtime_error* pointer, while the handler expects a reference to std::exception. The pointer does not fit it, there is no other handler, and std::terminate is called. Throw the object itself: throw std::runtime_error("disk full");.

Key points

  • The standard library throws exceptions itself: for example, at throws std::out_of_range.
  • All standard exceptions derive from std::exception, and what() returns the error text.
  • Your own exception class is declared as derived from std::runtime_error.
  • Throw the object itself and catch it by const reference: that way there is neither a dangling pointer nor slicing.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way