Module 13 — Summary¶
The essentials¶
- Exceptions make failure impossible to ignore by accident. A returned value can be discarded; a thrown object either reaches a handler or ends the program. That is a design change, not a syntax feature.
- Stack unwinding destroys every fully constructed local in every frame that is left, in reverse
order of construction. It runs destructors and nothing else — no
catchin a skipped frame, no statement-level cleanup, nofinally(C++ has none). - That is why RAII is the precondition for this module. Cleanup that must happen on all four paths
— success, early return, throw, and a constructor that threw — has to be a destructor. A
catchblock is for deciding, never for releasing. - A constructor that throws means the object never existed, so its own destructor will not run — but its already-constructed members' destructors will. Acquire into members, never into the constructor's body.
- Catch by
const&. By value you copy the exception and slice the derived part, which costs you the type and every accessor the handler needed (-Wcatch-valuewarns when it can). - Order handlers most-derived first. A base class matches everything derived from it, so the
specific handler below it is dead code (
-Wexceptionsnames the shadowing handler). - A hierarchy does two jobs: the type classifies the failure so a caller can catch broadly or
narrowly, and
what()carries the context a human needs. Keep it shallow. throw;rethrows the same object;throw error;copies it — and from a base reference, slices it again. When an outer handler must still see the original, rethrow rather than wrap.- Three guarantees: nothrow (cannot fail), strong (on failure, unchanged), basic (no leaks, still valid, contents may have changed). Write yours next to the declaration; a caller cannot recover correctly from a failure it did not know could change the object.
- The strong guarantee is copy-and-swap, Module 10's idiom with a second job: work on a copy,
then
swap— andswapisnoexcept, which is what makes the commit infallible. noexceptis part of the interface, checked by the compiler and read by the standard library. Break it and the program terminates without unwinding; declare it on an override looser than the base and the compiler stops you (error: looser exception specification on overriding virtual function).- Destructors are
noexceptby default, because they run during unwinding and two failures cannot both be reported. A destructor may log, swallow, or expose an explicitclose(); it may not throw, and the message you get when it does names the wrong failure. - Not every failure is an exception. Validate untrusted data once, at the boundary; return a value
for expected outcomes; throw when the operation cannot do what was asked;
assert(orstd::logic_error) when the caller is wrong. - Test the failure path, not just the success path. Produce the failure deliberately, count acquisitions and releases on both runs, and check the state afterwards. A failure path that has never run is not a design; it is a hope.
The one-sentence version¶
Let failures travel to the code that can act on them, guarantee cleanup with destructors rather than
catch blocks, state each operation's guarantee in the header — and then run the failure path on
purpose and count what happened.
Terms you must now be able to define¶
exception · throw · try / catch · propagation · stack unwinding ·
std::exception · std::logic_error / std::runtime_error · custom exception hierarchy ·
what() · catch by const& · slicing · rethrow · catch (...) ·
noexcept · std::terminate · exception-safety guarantee · nothrow · strong ·
basic · commit-or-rollback · [[nodiscard]] · boundary validation ·
failure path test
All of them are defined in the glossary under this module.
Before you move on¶
- [ ] All core exercises attempted, with the results checked
- [ ] The lab builds with no warnings and passes its checklist
- [ ] Both counter runs are recorded and balance — success and deliberate failure
- [ ] Every operation's guarantee is written next to its declaration, with no gaps
- [ ] Every failure in the lab was produced on purpose, at a line I chose
- [ ] The malformed-input case is reported (file and line) and the program continues
- [ ] Quiz score ≥ 80%, and every miss explained
- [ ] At least one challenge problem attempted — including the argument against using exceptions
- [ ] Skills checklist ticked, with the evidence for each item
- [ ] I can explain why a terminate message named the wrong failure
Where this goes next¶
Module 14 — The STL with OOP hands your objects to the standard
library, where every operation's guarantee, every noexcept move and every exception you allow out
of a comparator has a consequence you now know how to look up. It is also where the loops that survive
in the catalogue become algorithms — and where an invalidated iterator, which is this module's
"failure during a mutation" in a different costume, is finally named and demonstrated.
Module 15 — SOLID and Design Patterns puts this module's
judgement to work at a larger scale. A factory that can fail has to choose between a returned status,
an exception and a [[nodiscard]] pointer; a strategy whose implementation may fail must state what
it promises; and the dependency-inversion principle is what makes a failure path testable in the first
place — you cannot inject a failing component if the component is constructed inside the code under
test.
Module 16 — Capstone requires all of it: a corrupted input file, a design review, and a test suite in which the failure paths are exercised as deliberately as the success ones.
Further reading¶
- Effective Modern C++, Scott Meyers —
noexcept, and item-by-item reasoning about when a move can throw and what a container does about it. - Effective C++ (3rd ed.), Scott Meyers — the three guarantees, why destructors must not throw,
and why
what()deserves care. - The C++ Programming Language (4th ed.), Bjarne Stroustrup — exception handling as a design tool, including the argument for using it less than you might expect.
- cppreference: exceptions — the exact guarantee of any standard operation you call, which is the page to check before assuming.
- The C++ Core Guidelines, E section — the rules this module has been deriving from first principles, stated as rules.
Continue¶
Previous: Self-assessment · Next: Module 14 — The STL with OOP · Module home: Module 13