OOP · ENCT 151

Ch 9. Exception Handling

4 hours · 5 marks

9.1 Basics of Exception Handling

An exception is a run-time abnormal condition/error (divide by zero, invalid array index, file not found) that disrupts normal program flow. C++ exception handling uses three keywords:

try {
    int a = 10, b = 0;
    if (b == 0) throw runtime_error("Division by zero");
    cout << a / b;
} catch (runtime_error &e) {
    cout << "Caught: " << e.what();
}

9.2 Advantage Over Conventional Error Handling

9.3 Exception Handling Mechanism

When a throw executes: the program immediately stops normal execution, searches up the call stack for a catch block matching the exception's type, calls destructors for all local objects going out of scope along the way (stack unwinding), then transfers control into the matching catch block. If no matching handler is found anywhere in the call stack, std::terminate() is called and the program aborts.

9.4 Multiple Handlers

A single try block can be followed by multiple catch blocks, each handling a different exception type — the first matching handler (in order) is executed.

try {
    throw 3.14;                  // throws a double
} catch (int e) {
    cout << "int caught";
} catch (double e) {
    cout << "double caught: " << e;   // this one matches
} catch (char e) {
    cout << "char caught";
}

9.5 Catching All Exceptions

The ellipsis handler catch(...) matches any exception type, and is typically placed last as a catch-all fallback for unexpected/unknown exceptions.

try {
    throw "unexpected error";
} catch (int e) {
    cout << "int";
} catch (...) {                 // catches anything not matched above
    cout << "Unknown exception caught";
}

9.6 Rethrowing Exception

A catch block can perform partial handling (e.g., logging) and then re-throw the same exception using a bare throw; so an outer handler can also process it.

try {
    try {
        throw runtime_error("Disk error");
    } catch (runtime_error &e) {
        cout << "Logged: " << e.what();
        throw;                       // rethrow to outer handler
    }
} catch (runtime_error &e) {
    cout << "Outer handler: " << e.what();
}

9.7 Exception with Arguments

A thrown exception can carry data — a simple value, or a full object with multiple fields, describing the error in detail.

class MyException {
public:
    int errorCode;
    string message;
    MyException(int c, string m): errorCode(c), message(m) {}
};
try {
    throw MyException(404, "Not Found");
} catch (MyException &e) {
    cout << e.errorCode << ": " << e.message;
}

9.8 Exception Specification for Function

Older C++ allowed declaring which exceptions a function might throw: void f() throw(int, char); meaning it may only throw int or char. throw() with an empty list meant the function throws nothing. This mechanism is deprecated in modern C++ (C++11 onward) and replaced by the simpler noexcept specifier, which just declares whether a function may throw at all (void f() noexcept;).

9.9 Exceptions in Constructors and Destructors

9.10 Handling Uncaught Exceptions

If an exception is thrown but no matching catch block exists anywhere in the call stack, the special function std::terminate() is automatically invoked, which by default calls std::abort(), ending the program immediately (destructors of objects further up the stack are not guaranteed to run).

9.11 Handling Unexpected Exceptions

In the older exception-specification model, if a function threw an exception type not listed in its throw() specification, the special function std::unexpected() was called (which by default also calls terminate()). This mechanism is deprecated in modern C++ along with dynamic exception specifications; noexcept functions that throw now call std::terminate() directly.

Most Probable Questions

Q1. What is exception handling? Explain try, catch, and throw with a program. 6 marks
Exception handling manages run-time errors gracefully instead of crashing the program abruptly. try wraps risky code, throw signals an error, catch handles it. See the division-by-zero example in section 9.1.
Q2. What are the advantages of exception handling over traditional error handling (return codes)? 4 marks
See section 9.2: separates error-handling logic from normal logic, cannot be silently ignored, a single handler can cover many statements, errors can carry structured data, and errors automatically propagate up the call stack (stack unwinding) until handled.
Q3. Write a program demonstrating multiple catch blocks for different exception types. 6 marks
#include <iostream>
using namespace std;
int main() {
    try {
        int choice = 2;
        if (choice == 1) throw 10;
        else if (choice == 2) throw 3.14;
        else throw string("error");
    } catch (int e) {
        cout << "Integer exception: " << e;
    } catch (double e) {
        cout << "Double exception: " << e;
    } catch (string &e) {
        cout << "String exception: " << e;
    }
}
Q4. What is stack unwinding? Explain with reference to exception handling. 4 marks
Stack unwinding is the process that occurs after a throw: the runtime walks back up the function call stack looking for a matching catch block, and along the way properly calls the destructors of all local (automatic) objects in each function frame it passes through, ensuring resources are released even though the functions exit abnormally. See section 9.3.
Q5. What is rethrowing an exception? Why is it useful? 4 marks
Rethrowing (using a bare throw; inside a catch block) passes the same exception on to an outer handler after some local handling (e.g., logging or partial cleanup). It is useful when a function can only partially handle an error and wants a caller further up the stack to also be aware of and respond to it. See section 9.6.
Q6. Can constructors and destructors throw exceptions? Explain the precautions needed. 5 marks
Constructors can safely throw (e.g., to signal that initialization failed); the object's own destructor is not called, but already-constructed base/member sub-objects are properly destroyed during unwinding. Destructors should avoid throwing — if a destructor throws while another exception is already propagating, std::terminate() is called immediately and the program aborts, since C++ cannot manage two simultaneous exceptions. See section 9.9.
Q7. What is the purpose of catch(...)? 3 marks
catch(...) is a catch-all handler that matches any exception type, regardless of what was thrown. It is used, typically as the last handler, to guard against unexpected/unknown exceptions so the program can fail gracefully (e.g., log an error and exit cleanly) instead of crashing via terminate(). See section 9.5.
Q8. Write a program that defines a custom exception class carrying an error code and message. 6 marks
See the MyException example in section 9.7 — define a class with the needed fields and a constructor, throw an object of it, and catch it by reference to access its fields without slicing/copying overhead.
Q9. What happens if an exception is thrown but not caught anywhere in the program? 3 marks
std::terminate() is automatically invoked, which by default calls std::abort(), ending the program immediately; destructors of objects still on the stack above the throw point are not guaranteed to be called. See section 9.10.
← Chapter 8 Back to Index →