C++ Exceptions: try, catch and throw

An exception is a way to report an error. When code throws, the normal flow stops, and C++ goes up the calls until a matching catch.

#include <iostream>
#include <stdexcept>

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

int main() {
    try {
        std::cout << divide(10, 2) << '\n';
        std::cout << divide(1, 0) << '\n';
        std::cout << "not printed\n";
    } catch (const std::invalid_argument& e) {
        std::cout << "Caught: " << e.what() << '\n';
    }
    std::cout << "the program goes on\n";
}
Output

5
Caught: division by zero
the program goes on

Catch by const reference

Always catch by const&. Catching by value makes a copy and can slice the exception. Many catches are checked in order, and catch (...) catches everything.

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

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

    try {
        std::stoi("abc");
    } catch (const std::invalid_argument&) {
        std::cout << "invalid_argument\n";
    }

    try {
        throw 42;                               // you can throw any type, but do not
    } catch (...) {
        std::cout << "something else\n";
    }
}
Output

out_of_range
invalid_argument
something else

Custom exception

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

class InsufficientFunds : public std::runtime_error {
public:
    InsufficientFunds(const std::string& msg, double short_by)
        : std::runtime_error(msg), short_by(short_by) {}
    double short_by;
};

void withdraw(double balance, double amount) {
    if (amount > balance) throw InsufficientFunds("Not enough money", amount - balance);
}

int main() {
    try {
        withdraw(500, 800);
    } catch (const InsufficientFunds& e) {
        std::cout << e.what() << ", short by " << e.short_by << '\n';
    }
}
Output

Not enough money, short by 300

RAII makes exceptions safe

When an exception leaves a block, C++ calls the destructors of all the objects of that block, so smart pointers, strings and files are cleaned.

#include <iostream>
#include <stdexcept>

struct Guard {
    ~Guard() { std::cout << "cleaned up\n"; }
};

int main() {
    try {
        Guard g;
        throw std::runtime_error("boom");
    } catch (const std::exception& e) {
        std::cout << "caught " << e.what() << '\n';
    }
}
Output

cleaned up
caught boom

The order shows that cleaned up comes before caught: the stack is unwound first.

noexcept

noexcept promises that a function does not throw. If it does anyway, the program calls std::terminate. Move constructors should be noexcept.

std::expected (C++23)

For errors which are normal and expected (like a wrong input), you can return the result or the error, without exceptions:

#include <expected>
#include <iostream>
#include <string>

std::expected<int, std::string> parse(const std::string& s) {
    try {
        return std::stoi(s);
    } catch (...) {
        return std::unexpected("not a number: " + s);
    }
}

int main() {
    for (std::string text : {"42", "abc"}) {
        auto r = parse(text);
        if (r) std::cout << "value " << *r << '\n';
        else   std::cout << "error " << r.error() << '\n';
    }
}
Output

value 42
error not a number: abc

Note: Use exceptions for real errors that the caller can not continue without. Do not use them for normal flow. Next: C++ STL Containers.

Congratulations! Chapter Finished. Learn more about the similar topics:
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.