SyntaxStudy
Sign Up
C++ Beginner 1 min read

throw, try, and catch

C++ exception handling separates the code that detects an error from the code that handles it. A 'throw' expression raises an exception object and unwinds the call stack, running destructors for all local objects as it goes — this is stack unwinding. A 'try' block wraps code that may throw, and 'catch' handlers follow it, each matched against the type of the thrown object from most-derived to least-derived. Exceptions should be thrown for exceptional, unexpected conditions — not for normal control flow. The overhead of entering a try block is usually zero in modern compilers (zero-cost exception models), but the path through an exception handler is slower than a normal return. For performance-sensitive paths where errors are common, error codes or 'std::expected' (C++23) may be preferable. Catching by const reference ('catch (const std::exception& e)') is the idiomatic form. Catching by value causes slicing if the thrown type is derived from the caught type. A catch-all handler 'catch (...)' captures any exception type and is useful at process boundaries or in destructors to prevent exception propagation. Rethrowing with bare 'throw;' preserves the original exception type.
Example
#include <iostream>
#include <stdexcept>
#include <string>

double safeDivide(double a, double b) {
    if (b == 0.0)
        throw std::invalid_argument("Division by zero");
    return a / b;
}

int parseAge(const std::string& s) {
    int age = std::stoi(s);   // throws std::invalid_argument or std::out_of_range
    if (age < 0 || age > 150)
        throw std::out_of_range("Age out of plausible range");
    return age;
}

void riskyOperation() {
    try {
        double result = safeDivide(10.0, 0.0);
        std::cout << result << "\n";
    } catch (const std::invalid_argument& e) {
        std::cout << "Caught invalid_argument: " << e.what() << "\n";
        throw;   // rethrow to outer handler
    }
}

int main() {
    // Nested try/catch with rethrow
    try {
        riskyOperation();
    } catch (const std::exception& e) {
        std::cout << "Outer handler: " << e.what() << "\n";
    }

    // Multiple catch handlers, ordered most-derived first
    try {
        parseAge("-5");
    } catch (const std::out_of_range& e) {
        std::cout << "Out of range: " << e.what() << "\n";
    } catch (const std::invalid_argument& e) {
        std::cout << "Invalid argument: " << e.what() << "\n";
    } catch (const std::exception& e) {
        std::cout << "std::exception: " << e.what() << "\n";
    } catch (...) {
        std::cout << "Unknown exception\n";
    }

    return 0;
}