Skip to content

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 catch in a skipped frame, no statement-level cleanup, no finally (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 catch block 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-value warns 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 (-Wexceptions names 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 — and swap is noexcept, which is what makes the commit infallible.
  • noexcept is 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 noexcept by default, because they run during unwinding and two failures cannot both be reported. A destructor may log, swallow, or expose an explicit close(); 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 (or std::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