Exceptions and Error Handling
Report failures cleanly, recover safely, and keep your objects consistent
throw an error object; the language then unwinds the stack until a matching catch handles it. Done well, this keeps the "happy path" of your code clean and guarantees that resources are released even when things go wrong.
- use
throw,tryandcatchcorrectly, and throw objects derived fromstd::exception, - explain stack unwinding and why RAII is what makes exceptions safe,
- reason about the three exception-safety guarantees (basic, strong, nothrow) and mark code
noexcept, - decide when to use exceptions and when to prefer error codes such as
std::optionalorstd::error_code.
1. Why not just return an error code?
Every non-trivial function can fail: a file will not open, memory runs out, user input is nonsense, a network read times out. You have two broad ways to report that failure back to the caller.
- Error codes / sentinel values: the function returns a special value (
-1,nullptr,false, anenum) that the caller is supposed to check. - Exceptions: the function
throws an object; normal execution stops and control jumps to a handler.
The trouble with error codes is that they are easy to ignore. Nothing forces the caller to look at the return value, so a forgotten check silently corrupts the program. They also clutter every call site with if checks, and they cannot report failure from places that have no return channel, such as a constructor or an overloaded operator. Exceptions solve those problems: an unhandled exception cannot be silently dropped (it terminates the program instead), and the error type travels with a message and can carry data.