Skip to content

Quiz 13 — Exception Handling

Ten questions on making failure part of the design — and on the three ways exception handling usually goes wrong: catching by value, throwing from a destructor, and leaking on the way out.

Attempt all of them closed-book, then mark yourself against the answers.

Concepts: throw/catch · custom exceptions · catching by reference · stack unwinding · the basic, strong and nothrow guarantees · destructors and noexcept.


Q1. (MCQ) When an exception leaves a scope, what happens to that scope's local objects?

  • A. nothing; they leak
  • B. they are destroyed, in reverse order of construction
  • C. only those with a virtual destructor
  • D. the program terminates

Q2. (true/false) A destructor that throws during stack unwinding causes std::terminate.

Q3. (multiple-select) Which are the three exception-safety guarantees? Select all that apply.

  • A. Basic — invariants hold, no leaks
  • B. Strong — the operation either succeeds or has no effect
  • C. Nothrow — the operation never throws
  • D. Delayed — the exception is stored and thrown later

Q4. (predict) Write the two output lines.

#include <iostream>
#include <stdexcept>

int main() {
    try {
        throw std::runtime_error("boom");
    }
    catch (const std::logic_error&) {
        std::cout << "logic\n";
    }
    catch (const std::runtime_error& e) {
        std::cout << "runtime: " << e.what() << '\n';
    }
    std::cout << "after\n";
}

Q5. (predict) Write the output in order.

#include <iostream>
#include <stdexcept>

void inner() {
    throw std::out_of_range("limit");
}

int main() {
    try {
        try {
            inner();
        }
        catch (const std::out_of_range& e) {
            std::cout << "inner " << e.what() << '\n';
            throw;
        }
    }
    catch (const std::exception& e) {
        std::cout << "outer " << e.what() << '\n';
    }
    std::cout << "end\n";
}

Q6. (debug) Why does e.field() fail to compile, and what is the fix?

#include <iostream>
#include <stdexcept>
#include <string>

class ValidationError : public std::runtime_error {
public:
    explicit ValidationError(std::string field)
        : std::runtime_error("invalid " + field), field_(std::move(field)) {}
    const std::string& field() const { return field_; }
private:
    std::string field_;
};

int main() {
    try {
        throw ValidationError{"email"};
    }
    catch (std::runtime_error e) {
        e.field();
    }
}

Q7. (analysis) A Guard destructor throws when the stack is already unwinding. Describe what the program does, and state the rule this demonstrates.

Q8. (short answer) What does the strong guarantee promise, and how is it different from the basic guarantee?

Q9. (design) An order pipeline fails four ways: unknown customer, out of stock, invalid discount, payment timeout. Callers must retry any timeout with one catch and handle any invalid order with another. Sketch the hierarchy and say which type carries which extra data.

Q10. (analysis) A function news a buffer, does work, and may throw before it frees the buffer. Which guarantee does it currently provide, and what change moves it to the strong guarantee?


Scoring: mark yourself with the answers, then read the self-assessment guide.