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.
#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
- Write a function that throws if a withdrawal amount is negative.
- Catch
std::invalid_argumentand print the message. - Add a second catch for a different standard exception and explain why catch order matters when types are related.
- Explain how a local
std::stringis cleaned up if an exception leaves its scope.
Practice solutions
Show worked solutions
#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.#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 beforemain, and thetry/catchstatement is insidemain, where a handler can catch the exception.- Catch the more specific type before its base type. For example, catch
std::out_of_rangebeforestd::logic_error, becauseout_of_rangederives fromlogic_error. A base-class handler placed first would also catch the derived exception. - When the exception leaves the scope, stack unwinding destroys fully constructed automatic objects. The local
std::stringdestructor 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.