C++, End to End
Exceptions
Exceptions are C++'s mechanism for reporting errors that a function can't handle itself, without forcing every caller to check a return code by hand. They're powerful and genuinely controversial in some corners of the C++ world, for reasons worth understanding rather than dismissing.
The basic mechanics
throw raises an exception object. The runtime unwinds the call stack (destroying local objects along the way, in reverse order of construction) looking for a matching catch block. If none is found anywhere up the call stack, std::terminate is called and the program ends, so an uncaught exception is not a graceful failure.
Stack unwinding and why RAII makes this safe
As the stack unwinds looking for a handler, every local object that's gone out of scope gets destroyed normally, including any std::unique_ptr, std::vector, file handles wrapped in RAII types, and so on. This is exactly why RAII (resource acquisition is initialization, tying resource cleanup to an object's destructor) matters so much in C++: it's the mechanism that makes exception safety possible at all. A raw pointer with a manual delete somewhere later in the function will leak the moment an exception is thrown between the new and that delete; a unique_ptr never will, because its destructor runs regardless of how the function exits.
Exception safety guarantees
Code is described as offering one of a few levels of exception safety. The basic guarantee: if an exception is thrown, no resources leak and the program stays in a valid (if not necessarily predictable) state. The strong guarantee: if an exception is thrown, the operation has no effect at all, as if it was never attempted (a classic pattern: build the new state fully in a temporary, and only swap it into place at the very end, so a failure partway through never leaves things half-changed). The nothrow guarantee: the operation is guaranteed not to throw at all, and is marked noexcept accordingly.
noexcept and why it matters most for moves
Marking a function noexcept promises the compiler it will never throw. If it does anyway, std::terminate is called immediately (this is intentionally harsh: an exception escaping a noexcept function is treated as a programming error, not a recoverable condition). The place this matters most in practice: std::vector needs to move elements during reallocation, but can only safely do so if it can guarantee it won't be left in a half-moved state if something goes wrong partway through. If your move constructor isn't noexcept, vector will conservatively copy instead of move during reallocation, silently giving up the performance benefit from the previous chapter.
Catch by reference, not by value
catch (const std::exception& e) versus catch (const std::exception e): the second copies the exception object (and if the actual thrown type was more derived than std::exception, that copy slices it, the same slicing bug from the polymorphism chapter). Always catch by (const) reference.
The genuine debate: when not to use exceptions
Exceptions aren't free even when nothing throws: many ABIs add table-based unwinding metadata that affects code size and can inhibit certain optimizations, and throwing itself is comparatively expensive versus returning an error code. In tight, latency-sensitive hot loops, or in embedded/real-time contexts where an unbounded worst-case unwind is unacceptable, plenty of serious C++ codebases disable exceptions entirely (-fno-exceptions) and use return codes or types like std::expected (C++23) instead. This isn't a fringe opinion, it's a legitimate, widely used engineering tradeoff. For most application-level code, exceptions are the right default; know that the debate exists and why, rather than treating either side as automatically correct.
Try it yourself: exception safety with a failing constructor›
Modify the Resource example so a second resource's constructor throws partway through building a class with two members, and confirm that the first (successfully constructed) resource is still cleaned up correctly by the unwinding process.