C++ PROGRAMMING ยท 16 MIN STUDY

Exceptions and error handling

Report exceptional failures with throw and handle them with try and catch.

Learning objectives

  • Explain the purpose of exceptions.
  • Throw and catch a standard exception.
  • Distinguish expected input validation from exceptional failure.
  • Understand why exceptions should not be silently ignored.

Prerequisites: functions, return values, standard strings, and basic input validation. Understand that an error can be detected in one function and handled by its caller.

What is an exception?

An exception is a mechanism for reporting an error or unusual condition from a point where it is detected to a point that can handle it. C++ uses throw to signal an exception and try/catch to handle it.

C++17
#include <iostream>
#include <stdexcept>

int divide(int numerator, int denominator) {
    if (denominator == 0) {
        throw std::invalid_argument("denominator must not be zero");
    }
    return numerator / denominator;
}

int main() {
    try {
        std::cout << divide(10, 2) << '\n';
    } catch (const std::invalid_argument& error) {
        std::cerr << "Input error: " << error.what() << '\n';
        return 1;
    }
}

The function detects an invalid argument and throws. The caller surrounds the call with a try block and catches the matching exception type. what() provides a diagnostic message.

Stack unwinding and RAII

When an exception leaves a scope, C++ destroys fully constructed automatic objects in that scope as the stack unwinds. This is one reason RAII types are important: file streams, vectors, strings, and smart pointers release their resources automatically when their objects are destroyed.

Exceptions are not a substitute for every if statement

Use ordinary conditions for common expected choices, such as whether a user entered a valid menu option. Exceptions are useful when an operation cannot complete normally and the caller should decide how to respond. The right error-reporting design depends on the function's contract.

Common mistakes

  • Catching every exception and doing nothing.
  • Throwing a vague message that does not explain the failure.
  • Using exceptions for ordinary loop control.
  • Throwing pointers to exception objects rather than exception objects.
  • Forgetting that an exception may propagate beyond the current function if not caught.

Practice

  1. Write a function that throws if a withdrawal amount is negative.
  2. Catch std::invalid_argument and print the message.
  3. Add a second catch for a different standard exception and explain why catch order matters when types are related.
  4. Explain how a local std::string is cleaned up if an exception leaves its scope.

Practice solutions

Show worked solutions
  1. #include <stdexcept>
    void validate_withdrawal(double amount) {
        if (amount < 0) {
            throw std::invalid_argument("amount must not be negative");
        }
    }
    The function reports a violated argument requirement with a standard exception object.
  2. #include <iostream>
    #include <stdexcept>
    
    void validate_withdrawal(double amount) {
        if (amount < 0) {
            throw std::invalid_argument("amount must not be negative");
        }
    }
    
    int main() {
        try {
            validate_withdrawal(-5.0);
        } catch (const std::invalid_argument& error) {
            std::cerr << error.what() << '\n';
            return 1;
        }
    }
    This is a complete program: the function is defined before main, and the try/catch statement is inside main, where a handler can catch the exception.
  3. Catch the more specific type before its base type. For example, catch std::out_of_range before std::logic_error, because out_of_range derives from logic_error. A base-class handler placed first would also catch the derived exception.
  4. When the exception leaves the scope, stack unwinding destroys fully constructed automatic objects. The local std::string destructor runs and releases its owned memory automatically.

Check your understanding

What happens if no matching catch handler exists?

Answer: The exception continues propagating up the call stack. If it reaches the boundary of the program without being handled, the program calls std::terminate.

Summary

Exceptions separate detection from handling. Throw meaningful exception objects, catch specific types where recovery is possible, and use RAII so resources are released during normal returns and stack unwinding.

References