In C++, handling unexpected runtime errors can be accomplished using exceptions. While exceptions provide a clean separation between business logic and error handling, they carry major compiler costs that every systems developer must understand.
Exception Syntax: Try, Throw, and Catch
To handle errors with exceptions, you wrap code that might fail in a try block. If an error is detected, you throw an exception object. A matching catch block catches the exception by reference to handle it. The C++ standard library provides common exception types in <stdexcept>, such as std::invalid_argument and std::runtime_error, which support the .what() method to query the error message string.
How Exceptions Work: The Happy Path vs The Sad Path
C++ exceptions use a model known as the Zero Cost Exception Model. This name is slightly misleading. It means that as long as no exception is thrown (the happy path), execution experiences zero runtime overhead. There are no active branch checks or registers allocated to exception tracking.
However, when an exception is thrown (the sad path), execution speeds drop significantly. The operating system must perform stack frame unwinding:
- The runtime stops normal code execution.
- It scans compiler generated lookup tables (exception tables) to locate a catch block matching the thrown type.
- It steps backward through active stack frames, calling destructors for all local variables in those frames to prevent memory leaks.
- It restores CPU registers to the state of the catch block and resumes execution.
This unwinding process can take thousands of CPU cycles, making exceptions extremely slow for regular flow control.
Binary Size Overhead and .eh_frame
Implementations often emit unwind metadata, such as .eh_frame, so stack unwinding can run destructors. The binary-size cost varies by ABI, compiler, and program. Some constrained systems disable exceptions, but that is an architectural decision with API and error-handling consequences, not a universal performance requirement.
- The Emergency Train Brake Metaphor: Throwing an exception is like pulling the emergency brake on a high speed train. It stops the train and keeps everyone safe, but the sudden friction damages the wheels, and resetting the systems takes a long time. You would never pull the brake just to slow down for a station (standard code flow).