Quiz 13 — Answers¶
Q1 — B. Leaving a scope by exception destroys that scope's local objects, in reverse order of construction — the same as a normal exit. This is stack unwinding, and it is what makes RAII work on the failure path.
Q2 — True. A destructor is implicitly noexcept (since C++11). If one throws while the
stack is already unwinding, there would be two live exceptions, so the runtime calls
std::terminate. GCC also warns at compile time with -Wterminate.
Q3 — A, B, C. The three guarantees are basic (A), strong (B) and nothrow (C). There is no "delayed" guarantee (D).
Q4 —
std::runtime_error is not derived from std::logic_error — they are siblings under
std::exception — so the first handler does not match. The second matches and prints the
message; the program then continues after the try/catch, printing "after".
Q5 —
The inner handler catches, prints, and throw; rethrows the same exception object to the
outer std::exception handler, which prints the same what(). Then the program continues
after the outer try/catch.
Q6 — The catch (std::runtime_error e) parameter is by value, so the ValidationError
is sliced down to its std::runtime_error base and the field_ member is lost — hence
error: 'class std::runtime_error' has no member named 'field'. Fix: catch by
const ValidationError&:
Catching by const reference also avoids a copy and never slices.
Q7 — The Guard destructor runs during unwinding and throws a second exception; because
a destructor is noexcept, the runtime calls std::terminate and the program aborts — the
outer catch never runs. The rule: a destructor must never throw. Cleanup that can fail
should be done outside a destructor, or must swallow the failure and report it another way.
Q8 — The strong guarantee means the operation either completes successfully or leaves the program exactly as it was (no effect) — it is all-or-nothing. The basic guarantee is weaker: invariants still hold and no resources leak, but the object may be in a changed (though valid) state after a failure.
Q9 — A common base OrderError : std::runtime_error, with UnknownCustomer,
OutOfStock, InvalidDiscount and PaymentTimeout deriving from it. Callers can
catch (const PaymentTimeout&) to retry any timeout with one clause, and
catch (const OrderError&) (or a sub-hierarchy under it) to handle any invalid order.
PaymentTimeout carries the attempt count or endpoint; InvalidDiscount carries the code
that failed. A caller can also catch std::exception as a last resort.
Q10 — It provides at best the basic guarantee (probably not even that — the buffer
leaks, so it fails basic too). To reach the strong guarantee, hold the buffer in a
unique_ptr (or build a replacement first) so that when the function throws, the resource is
released by the destructor and the caller's state is untouched — the operation has no
lasting effect.
Marking. One point per question (Q3 all-or-nothing). Then read Marking Yourself.