C++ Cheatsheet
Exceptions
Use this C++ reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Throw and Catch
#include <stdexcept> #include <exception> // Throw any copyable type throw 42; // throw int (unusual) throw std::runtime_error("oops"); // throw an exception object (preferred) throw; // rethrow current exception (in catch block only) // Catch block: matches by type (most-derived first) try { doSomethingRisky(); } catch (const std::invalid_argument& e) { // specific type first std::cerr << "invalid arg: " << e.what() << "\n"; } catch (const std::runtime_error& e) { std::cerr << "runtime error: " << e.what() << "\n"; } catch (const std::exception& e) { // base class std::cerr << "exception: " << e.what() << "\n"; } catch (...) { // catch-all (last resort) std::cerr << "unknown exception\n"; throw; // rethrow to preserve original exception }
Standard Exception Hierarchy (<stdexcept>)
std::exception ├── std::bad_alloc (new fails; <new>) ├── std::bad_cast (dynamic_cast fails; <typeinfo>) ├── std::bad_typeid (<typeinfo>) ├── std::bad_function_call (calling empty std::function) ├── std::bad_optional_access (accessing empty optional; C++17) ├── std::bad_variant_access (get on wrong type; C++17) ├── std::logic_error │ ├── std::invalid_argument │ ├── std::domain_error │ ├── std::length_error │ ├── std::out_of_range │ └── std::future_error (<future>) └── std::runtime_error ├── std::range_error ├── std::overflow_error ├── std::underflow_error └── std::system_error (<system_error>)
#include <stdexcept> throw std::invalid_argument("parameter must be positive"); throw std::out_of_range("index " + std::to_string(i) + " out of bounds"); throw std::runtime_error("failed to open file"); throw std::length_error("container too large"); throw std::overflow_error("arithmetic overflow"); // std::system_error (OS errors) #include <system_error> throw std::system_error(errno, std::generic_category(), "read failed"); // Access what() try { /* ... */ } catch (const std::exception& e) { std::cerr << e.what(); }
Custom Exception Classes
#include <stdexcept> #include <string> // Minimal custom exception class DatabaseError : public std::runtime_error { public: explicit DatabaseError(const std::string& msg) : std::runtime_error(msg) {} }; // With extra data class HttpError : public std::runtime_error { int code_; public: HttpError(int code, const std::string& msg) : std::runtime_error(msg), code_(code) {} int code() const noexcept { return code_; } }; // Usage try { throw HttpError(404, "Not Found"); } catch (const HttpError& e) { std::cerr << e.code() << " " << e.what() << "\n"; } catch (const std::exception& e) { std::cerr << e.what() << "\n"; }
noexcept
// Declare that a function will never throw // If it does, std::terminate() is called (no unwinding) int safe() noexcept { return 42; } // Conditional noexcept template<typename T> void swap(T& a, T& b) noexcept(std::is_nothrow_swappable_v<T>) { using std::swap; swap(a, b); } // Query at compile time constexpr bool n1 = noexcept(safe()); // true constexpr bool n2 = noexcept(risky()); // false if risky() is not noexcept // Move operations should be noexcept for std::vector reallocation optimization class MyClass { public: MyClass(MyClass&&) noexcept = default; MyClass& operator=(MyClass&&) noexcept = default; };
Exception Safety Guarantees
| Level | Guarantee |
|---|---|
| No-throw | Never throws; if it does, std::terminate (mark noexcept) |
| Strong | Either succeeds completely or state is unchanged (commit-or-rollback) |
| Basic | On exception, program is in a valid (but unspecified) state; no leaks |
| None | No guarantee — avoid |
Copy-and-swap idiom (strong guarantee for assignment):
class Buffer { int* data_; std::size_t size_; public: // swap: noexcept exchange of internals friend void swap(Buffer& a, Buffer& b) noexcept { using std::swap; swap(a.data_, b.data_); swap(a.size_, b.size_); } // Copy assignment with strong guarantee: Buffer& operator=(Buffer other) { // copy made in parameter (may throw there) swap(*this, other); // noexcept exchange return *this; // old data destroyed in 'other' at scope end } };
Stack Unwinding and RAII
When an exception is thrown, the stack is unwound: destructors of all local objects are called in reverse order of construction. This is why RAII-based resource management is exception-safe.
void process() { std::unique_ptr<Widget> w = std::make_unique<Widget>(); // RAII std::vector<int> v = {1, 2, 3}; throw std::runtime_error("fail"); // ↑ unique_ptr and vector destructors still run — no leak } // Destructors MUST NOT throw. If they do and the stack is already // unwinding, std::terminate() is called. class Safe { public: ~Safe() noexcept { // destructor is implicitly noexcept; make it explicit try { cleanup(); } catch (...) {} // swallow all exceptions in destructor } };
Nested Exceptions (C++11)
// Catch, add context, and rethrow with nested original exception void inner() { throw std::runtime_error("inner problem"); } void outer() { try { inner(); } catch (...) { std::throw_with_nested(std::runtime_error("outer context")); } } // Print full chain void printNested(const std::exception& e, int depth = 0) { std::cerr << std::string(depth * 2, ' ') << e.what() << "\n"; try { std::rethrow_if_nested(e); // rethrows nested if present } catch (const std::exception& inner) { printNested(inner, depth + 1); } catch (...) {} } try { outer(); } catch (const std::exception& e) { printNested(e); }
std::exception_ptr — Store and Transfer Exceptions
#include <exception> std::exception_ptr eptr; try { throw std::runtime_error("stored"); } catch (...) { eptr = std::current_exception(); // capture any active exception } // ... later / in another thread ... if (eptr) { try { std::rethrow_exception(eptr); // rethrow the stored exception } catch (const std::exception& e) { std::cerr << e.what() << "\n"; } } // Used in std::promise/std::future to propagate exceptions across threads
std::terminate and Termination
// Termination is called when: // - An exception is thrown and not caught // - A noexcept function throws // - An exception is thrown during stack unwinding (second active exception) // - A pure virtual function is called // - std::terminate() is called directly // Install custom terminate handler std::set_terminate([](){ std::cerr << "Unhandled exception!\n"; std::abort(); });
Best Practices
- Throw by value, catch by const reference:
throw std::runtime_error("msg"); // by value catch (const std::runtime_error& e) { e.what(); } // by const& // Never catch by value: catch (std::runtime_error e) — copies and may slice // Never catch by pointer: catch (std::runtime_error* e)
- Catch most-derived types first — base class handlers shadow derived ones.
- Destructors must not throw — mark them
noexcept. - Move operations should be
noexcept— required for strong guarantee instd::vectorresize. - Use RAII — never
newwithout a smart pointer; never acquire resources without a destructor to release them. catch (...)at the top level — log and abort; never silently swallow exceptions.if constexprover exception for control flow — exceptions are for truly exceptional conditions, not normal program logic.