Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

C++ Exceptions

Errors happen: a file is missing, a number is divided by zero, the user types text instead of a number. Exceptions let the code that finds a problem report it, and the code that can handle it decide what to do, even several function calls higher up.

throw, try and catch

#include <iostream>
#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, 4) << "\n";
        std::cout << divide(1, 0) << "\n";     // throws: the rest of the try block is skipped
        std::cout << "not printed\n";
    } catch (const std::invalid_argument& e) {
        std::cout << "Error: " << e.what() << "\n";
    }
    std::cout << "The program goes on.\n";
    return 0;
}
Output
2.5
Error: cannot divide by zero
The program goes on.

throw stops the current function at once, and every function between it and the matching catch. Catch exceptions by const&; what() returns the message.

Exceptions from the standard library

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

int main() {
    std::vector<int> v = {1, 2, 3};
    try {
        std::cout << v.at(5) << "\n";
    } catch (const std::out_of_range&) {
        std::cout << "index outside the vector\n";
    }
    try {
        int n = std::stoi("forty");
        std::cout << n << "\n";
    } catch (const std::invalid_argument&) {
        std::cout << "not a number\n";
    }
    return 0;
}
Output
index outside the vector
not a number

The standard exceptions form a family: out_of_range and invalid_argument are kinds of logic_error, which is a kind of exception. catch (const std::exception& e) catches them all, so put specific catch blocks first and the general one last. catch (...) catches anything.

Clean-up happens automatically

While an exception travels up, the destructors of all local objects in between run, so files close and memory is freed. This is why C++ code uses objects like std::vector, std::string, file streams and smart pointers instead of raw resources: they clean up even when an exception is thrown.

Your own exceptions

Derive from a standard exception class to add your own kinds of errors:

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

class InsufficientFunds : public std::runtime_error {
public:
    explicit InsufficientFunds(double needed)
        : std::runtime_error("not enough money: " + std::to_string(static_cast<int>(needed)) + " more needed") {}
};

void withdraw(double& balance, double amount) {
    if (amount > balance) throw InsufficientFunds(amount - balance);
    balance -= amount;
}

int main() {
    double balance = 500;
    try {
        withdraw(balance, 200);
        withdraw(balance, 400);
    } catch (const InsufficientFunds& e) {
        std::cout << e.what() << "\n";
    }
    std::cout << "balance " << balance << "\n";
    return 0;
}
Output
not enough money: 100 more needed
balance 300

Uncaught exceptions

If no catch matches, the program stops. In our editor the terminal shows the same message a g++ program prints, terminate called after throwing an instance of … with the what() text, and the exit code 134.

When to use exceptions

Use them for real errors that the current function cannot handle: a missing file, invalid data, a broken connection. Do not use them for normal results such as "not found"; return a value (or std::optional) for those.

Practice exercises

  1. Write a function that reads an age and throws std::out_of_range when it is below 0 or above 150, and catch it in main.
  2. Keep asking the user for a number with getline and stoi until the conversion works.
  3. Remove the try around v.at(5) and read what the terminal prints.