AnyCompiler

Exceptions and Error Handling

A function that encounters an error it can't handle locally can throw an exception — execution immediately unwinds up the call stack until it finds a matching catch block. A throw with no matching catch anywhere terminates the program.

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(double a, double b) {
    if (b == 0) {
        throw runtime_error("cannot divide by zero");
    }
    return a / b;
}

int main() {
    try {
        cout << divide(10, 2) << endl;
        cout << divide(10, 0) << endl;
    } catch (const runtime_error& e) {
        cout << "Error: " << e.what() << endl;
    }
    return 0;
}
Output
5
Error: cannot divide by zero

Notice divide(10, 2) prints its result normally, but divide(10, 0) throws before it can return — execution jumps straight to the catch block, skipping the rest of the try block entirely. As the stack unwinds past any RAII objects along the way, their destructors still run normally, so resources get cleaned up even when an exception cuts a function short.

That's the foundation of C++: references and pointers for flexible access without always copying, RAII for deterministic resource cleanup without a garbage collector, classes with virtual dispatch for polymorphism, and templates for compile-time generic code. From here, the best next step is practice — pick a small program and build it, paying close attention to who owns each resource and when it gets cleaned up.

Practice exercise

Write a function safeDivide(double a, double b) that throws a runtime_error("cannot divide by zero") if b is 0, otherwise returns a / b. Call it with (10, 0) inside a try/catch, and print the caught exception's message.

← Smart PointersMore lessons coming soon