Site

Exceptions — try, catch, throw

Tutorial S4.0  •  C++ / Learn / StdLib

S4.0 What This Teaches

Exceptions separate error detection from error handling. This tutorial covers:

S4.1 try, catch, throw

double safe_divide(double a, double b) {
    if (b == 0.0)
        throw std::domain_error("division by zero");
    return a / b;
}

try {
    double r = safe_divide(10.0, 0.0);
} catch (const std::domain_error& e) {
    std::cout << "caught: " << e.what() << "\n";
}
throw unwinds the call stack until a matching catch is found. Destructors of all local objects are called along the way (stack unwinding) - this is why RAII works with exceptions.

S4.2 Standard Exception Hierarchy

ExceptionWhen thrown by
std::exceptionBase class; provides what()
std::runtime_errorErrors detected at runtime
std::logic_errorErrors in program logic (bugs)
std::invalid_argumentBad argument value
std::out_of_rangevector::at, string::at, etc.
std::bad_allocnew fails to allocate memory
std::domain_errorMathematical domain errors
Catch by const std::exception& to catch any standard exception. Order catch clauses from most specific to least specific.

S4.3 Custom Exception Types

class ValidationError : public std::runtime_error {
public:
    explicit ValidationError(const std::string& msg)
        : std::runtime_error(msg) {}
};

int parse_positive(const std::string& s) {
    int n = std::stoi(s);
    if (n <= 0)
        throw ValidationError("value must be positive, got: " + s);
    return n;
}

try {
    parse_positive("-5");
} catch (const ValidationError& e) {
    std::cout << "validation: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "other error: " << e.what() << "\n";
}

S4.4 noexcept

int add(int a, int b) noexcept { return a + b; }

// move constructors should be noexcept
class Buffer {
public:
    Buffer(Buffer&& other) noexcept : data_(other.data_) {
        other.data_ = nullptr;
    }
private:
    int* data_;
};
noexcept tells the compiler the function will not throw. If it does throw, std::terminate is called. Use it on move constructors, move assignment operators, and simple utility functions. The standard library uses noexcept to enable optimizations.

S4.5 Example - All Together

// Exceptions - try/catch/throw, standard hierarchy, custom exceptions.

#include <iostream>
#include <stdexcept>
#include <string>

class ValidationError : public std::runtime_error {
public:
    explicit ValidationError(const std::string& msg)
        : std::runtime_error(msg) {}
};

int parse_positive(const std::string& s) {
    int n = std::stoi(s);
    if (n <= 0) throw ValidationError("must be positive, got: " + s);
    return n;
}

int main() {
    for (const auto& input : {"42", "-5", "abc"}) {
        try {
            std::cout << input << " -> " << parse_positive(input) << "\n";
        } catch (const ValidationError& e) {
            std::cout << "validation: " << e.what() << "\n";
        } catch (const std::exception& e) {
            std::cout << "error: " << e.what() << "\n";
        }
    }
    return 0;
}
42 -> 42
validation: must be positive, got: -5
error: stoi

S4.6 Exercise

Exercise
  • Write a Stack<T> class that throws std::underflow_error on pop() or top() when empty.
  • Wrap std::stod in a function that catches std::invalid_argument and returns a std::optional<double> instead of throwing.
  • Mark a simple arithmetic function noexcept and verify with noexcept(f(1,2)) that the expression is noexcept.

S4.7 Common Mistakes

Catching by value instead of reference

catch (std::exception e) { }   // slices: e loses the derived type information
catch (const std::exception& e) { }  // correct

Catching ... without re-throwing in destructors

If an exception is already propagating and a destructor throws another exception, std::terminate is called. Keep destructors noexcept.

Using exceptions for normal control flow

Exception handling has overhead. Do not use throw/catch to implement expected branching (e.g., end-of-loop detection). Reserve exceptions for genuinely exceptional conditions.

S4.8 Key Terms

TermMeaning
throwRaises an exception; begins stack unwinding
tryBlock that may contain throwing code
catchHandles a specific exception type
stack unwindingDestroying local objects as the call stack is popped after a throw
what()Member of std::exception; returns the error message
noexceptDeclares a function will not throw; enables compiler optimizations
std::terminateCalled when an exception escapes a noexcept function or a destructor throws