Exception Handling in C++
A practical guide to exception handling in C++ — how try, catch, and throw work together, standard exception types, writing custom exceptions, and how exceptions interact with RAII.

ot every error can be checked in advance — a file might not exist, a network call might fail, a user might pass in invalid input. C++'s exception handling mechanism exists to deal with these situations without littering every function with manual error-code checks.
The Three Keywords
try— wraps code that might failthrow— signals that something has gone wrong, and hands off an exception object describing itcatch— receives that exception object and decides how to respond
cpp#include <iostream> #include <stdexcept> double divide(double a, double b) { if (b == 0) { throw std::runtime_error("Division by zero"); } return a / b; } int main() { try { double result = divide(10, 0); std::cout << result; } catch (const std::runtime_error& e) { std::cout << "Error: " << e.what() << std::endl; } }
When divide() throws, execution immediately stops at that point and jumps to the nearest matching catch block — skipping everything else in try in between.
Standard Exception Types
C++'s <stdexcept> header provides a hierarchy of ready-made exception types instead of forcing every project to invent its own:
std::runtime_error— errors detectable only at runtimestd::logic_error— errors from flawed program logicstd::out_of_range— accessing an invalid index or rangestd::invalid_argument— a function received an unacceptable argumentstd::bad_alloc— memory allocation failed All of these inherit fromstd::exception, which is why catchingconst std::exception&will catch any of them.
Catching Multiple Exception Types
cpptry { riskyOperation(); } catch (const std::out_of_range& e) { std::cout << "Out of range: " << e.what(); } catch (const std::exception& e) { std::cout << "General error: " << e.what(); }
Order matters here — more specific exception types should be caught before more general ones, since the first matching catch block wins.
Writing a Custom Exception
For domain-specific errors, define your own exception class by inheriting from std::exception:
cppclass InsufficientFundsError : public std::exception { public: const char* what() const noexcept override { return "Insufficient funds for this transaction"; } }; void withdraw(double balance, double amount) { if (amount > balance) { throw InsufficientFundsError(); } }
This lets calling code catch and respond to your specific error type, rather than parsing a generic message string.
Exceptions and RAII: Why They're Designed Together
One of the strongest reasons C++ exceptions work well is their interaction with RAII (see the constructors/destructors post for the full pattern): when an exception is thrown, C++ guarantees that every local object already constructed on the way to the throw gets its destructor called during "stack unwinding" — so files get closed, locks get released, and memory gets freed automatically, even though the function never reached its normal end.
cppvoid process() { std::lock_guard<std::mutex> lock(mtx); // acquires a lock riskyOperation(); // if this throws... } // ...the lock is still released automatically during unwinding
When Not to Use Exceptions
Exceptions carry real runtime cost and are best reserved for genuinely exceptional situations — not routine control flow. Using them for predictable, frequent outcomes (like "user typed an invalid menu option") is generally considered worse practice than a simple return value or error code, both for performance and for readability.
The Takeaway
try, throw, and catch give C++ a clean way to separate "the code that does the work" from "the code that decides what to do when something goes wrong" — and because they're built to cooperate with RAII, they let you handle failures without leaking resources along the way.


