devnotes.

Exception Handling

C++ handles error conditions with try/catch/throw, letting error-handling code live separately from the normal logical flow of a function.

#include <stdexcept>

double divide(double a, double b) {
    if (b == 0) {
        throw std::invalid_argument("Cannot divide by zero");
    }
    return a / b;
}

int main() {
    try {
        std::cout << divide(10, 0) << "\n";
    } catch (const std::invalid_argument &e) {   // catch by const reference
        std::cerr << "Error: " << e.what() << "\n";
    } catch (const std::exception &e) {           // catches anything else standard
        std::cerr << "Unexpected error: " << e.what() << "\n";
    }
    std::cout << "Program continues\n";
    return 0;
}

throw raises an exception — any object can technically be thrown, but throwing something derived from std::exception (from <stdexcept> or <exception>) is the idiomatic choice, since it guarantees a .what() method returning a human-readable description of the problem.

Exceptions should always be caught by const reference (catch (const std::exception &e)), never by value — catching by value causes an unnecessary copy and, more seriously, "slices" a derived exception type down to its base type, silently discarding extra information. Multiple catch blocks are checked in order, so more specific exception types should be caught before more general ones (as above, invalid_argument before the general std::exception), since the first matching catch wins and later ones are skipped.

noexcept (C++11) on a function signature documents — and lets the compiler optimize based on — a promise that the function will never throw; if it does anyway, the program terminates immediately via std::terminate rather than unwinding normally, so it should only be used when you're genuinely certain no exception can escape.

Exception Handling in C++ | DevNotes