Chapter 19 Advanced 52 Questions

Practice Questions — Exception Handling (try, catch, throw)

← Back to Notes
10 Easy
9 Medium
8 Hard

Topic-Specific Questions

Question 1
Easy
What is the output of the following code?
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
    try {
        cout << "A" << endl;
        throw runtime_error("error");
        cout << "B" << endl;
    } catch (const runtime_error& e) {
        cout << "C: " << e.what() << endl;
    }
    cout << "D" << endl;
    return 0;
}
After throw, execution skips to the catch block. Code after throw in the try block is skipped.
A
C: error
D
Question 2
Easy
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        throw 42;
    } catch (int e) {
        cout << "Caught: " << e << endl;
    }
    return 0;
}
You can throw any type in C++, including integers.
Caught: 42
Question 3
Easy
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
    try {
        throw out_of_range("index 10");
    } catch (const exception& e) {
        cout << e.what() << endl;
    }
    return 0;
}
out_of_range inherits from exception. catch(const exception&) catches it.
index 10
Question 4
Easy
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        cout << "Before" << endl;
        throw "Error message";
    } catch (const char* msg) {
        cout << msg << endl;
    }
    cout << "After" << endl;
    return 0;
}
A string literal "Error message" is of type const char*.
Before
Error message
After
Question 5
Easy
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        try {
            throw 100;
        } catch (int e) {
            cout << "Inner: " << e << endl;
            throw;  // Rethrow
        }
    } catch (int e) {
        cout << "Outer: " << e << endl;
    }
    return 0;
}
throw; rethrows the same exception to the outer try-catch.
Inner: 100
Outer: 100
Question 6
Medium
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
void func(int x) {
    if (x < 0) throw invalid_argument("Negative");
    if (x == 0) throw runtime_error("Zero");
    cout << "OK: " << x << endl;
}
int main() {
    int vals[] = {5, 0, -1};
    for (int i = 0; i < 3; i++) {
        try {
            func(vals[i]);
        } catch (const invalid_argument& e) {
            cout << "Invalid: " << e.what() << endl;
        } catch (const runtime_error& e) {
            cout << "Runtime: " << e.what() << endl;
        }
    }
    return 0;
}
5 is valid. 0 throws runtime_error. -1 throws invalid_argument.
OK: 5
Runtime: Zero
Invalid: Negative
Question 7
Medium
What is the output?
#include <iostream>
using namespace std;
class Obj {
    string name;
public:
    Obj(string n) : name(n) { cout << name << " created" << endl; }
    ~Obj() { cout << name << " destroyed" << endl; }
};
void bar() {
    Obj o2("B");
    throw 1;
}
void foo() {
    Obj o1("A");
    bar();
}
int main() {
    try {
        foo();
    } catch (int) {
        cout << "Caught" << endl;
    }
    return 0;
}
Stack unwinding: B is destroyed first, then A, as the exception propagates up.
A created
B created
B destroyed
A destroyed
Caught
Question 8
Medium
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        throw 3.14;
    } catch (int e) {
        cout << "int: " << e << endl;
    } catch (double e) {
        cout << "double: " << e << endl;
    } catch (...) {
        cout << "unknown" << endl;
    }
    return 0;
}
3.14 is a double. catch(int) does not match because there is no implicit conversion for exception matching.
double: 3.14
Question 9
Medium
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
void process() {
    try {
        throw runtime_error("Error");
    } catch (const exception& e) {
        cout << "Caught: " << e.what() << endl;
    }
    cout << "After catch in process" << endl;
}
int main() {
    try {
        process();
        cout << "After process" << endl;
    } catch (const exception& e) {
        cout << "Main caught: " << e.what() << endl;
    }
    return 0;
}
The exception is caught inside process(). It does not propagate to main().
Caught: Error
After catch in process
After process
Question 10
Hard
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
class Base : public exception {
public:
    const char* what() const noexcept override { return "Base"; }
};
class Derived : public Base {
public:
    const char* what() const noexcept override { return "Derived"; }
};
int main() {
    try {
        throw Derived();
    } catch (const Base& e) {
        cout << e.what() << endl;
    }
    try {
        throw Derived();
    } catch (Base e) {
        cout << e.what() << endl;
    }
    return 0;
}
First catch is by reference (polymorphic). Second catch is by value (slicing).
Derived
Base
Question 11
Hard
What is the output?
#include <iostream>
using namespace std;
class Guard {
    string name;
public:
    Guard(string n) : name(n) { cout << "Lock " << name << endl; }
    ~Guard() { cout << "Unlock " << name << endl; }
};
void c() { Guard g("C"); throw 1; }
void b() { Guard g("B"); c(); }
void a() { Guard g("A"); b(); }
int main() {
    try { a(); }
    catch (int) { cout << "Handled" << endl; }
    return 0;
}
Three levels of function calls, each with a Guard. Exception in c() unwinds through b() and a().
Lock A
Lock B
Lock C
Unlock C
Unlock B
Unlock A
Handled
Question 12
Hard
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
class MyErr : public runtime_error {
    int code;
public:
    MyErr(int c, const string& m) : runtime_error(m), code(c) {}
    int getCode() const { return code; }
};
void test(int x) {
    if (x == 1) throw MyErr(404, "Not Found");
    if (x == 2) throw MyErr(500, "Server Error");
    if (x == 3) throw runtime_error("Generic");
}
int main() {
    for (int i = 1; i <= 3; i++) {
        try {
            test(i);
        } catch (const MyErr& e) {
            cout << e.getCode() << ": " << e.what() << endl;
        } catch (const runtime_error& e) {
            cout << "runtime: " << e.what() << endl;
        }
    }
    return 0;
}
MyErr is more specific than runtime_error. Catch order: MyErr first, runtime_error second.
404: Not Found
500: Server Error
runtime: Generic
Question 13
Hard
What is the output?
#include <iostream>
using namespace std;
void func() noexcept {
    cout << "In noexcept function" << endl;
}
int main() {
    cout << noexcept(func()) << endl;
    cout << noexcept(throw 1) << endl;
    func();
    return 0;
}
noexcept(expr) returns true if expr cannot throw. func() is noexcept. throw 1 is not.
1
0
In noexcept function
Question 14
Easy
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
    try {
        throw invalid_argument("bad input");
    } catch (const invalid_argument& e) {
        cout << e.what() << endl;
    }
    cout << "done" << endl;
    return 0;
}
invalid_argument is caught by the matching handler. Program continues after catch.
bad input
done
Question 15
Medium
What is the output?
#include <iostream>
using namespace std;
class Cleanup {
    string name;
public:
    Cleanup(string n) : name(n) { cout << "+" << name << " "; }
    ~Cleanup() { cout << "-" << name << " "; }
};
void risky() {
    Cleanup c("R");
    throw 1;
}
int main() {
    Cleanup c("M");
    try {
        risky();
    } catch (int) {
        cout << "caught ";
    }
    cout << "end" << endl;
    return 0;
}
M is created in main. R is created in risky, then destroyed during unwinding. M is destroyed at end of main.
+M +R -R caught end -M
Question 16
Medium
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
    try {
        try {
            throw runtime_error("deep");
        } catch (const logic_error& e) {
            cout << "logic: " << e.what() << endl;
        }
    } catch (const runtime_error& e) {
        cout << "runtime: " << e.what() << endl;
    }
    return 0;
}
The inner catch expects logic_error but receives runtime_error. They are different branches of the exception hierarchy.
runtime: deep
Question 17
Hard
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
class MyEx : public runtime_error {
    int code;
public:
    MyEx(int c) : runtime_error("Error " + to_string(c)), code(c) {}
    int getCode() const { return code; }
};
void test() {
    try {
        throw MyEx(404);
    } catch (const runtime_error& e) {
        cout << e.what() << endl;
        throw;
    }
}
int main() {
    try {
        test();
    } catch (const MyEx& e) {
        cout << "Code: " << e.getCode() << endl;
    }
    return 0;
}
test catches as runtime_error&, logs, rethrows. main catches as MyEx&. throw; preserves the original type.
Error 404
Code: 404

Mixed & Application Questions

Question 1
Easy
What are the three keywords used for exception handling in C++?
Think about wrapping code, creating exceptions, and handling them.
try (wraps code that might throw), throw (creates and throws an exception), and catch (catches and handles the exception).
Question 2
Easy
Why should you catch exceptions by const reference instead of by value?
Think about what happens to a derived exception when caught as a base class value.
Catching by value creates a copy and slices derived exceptions to the base type, losing polymorphism and context data. Catching by const reference preserves the original exception type and avoids unnecessary copies.
Question 3
Easy
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        cout << "A" << endl;
    } catch (...) {
        cout << "B" << endl;
    }
    cout << "C" << endl;
    return 0;
}
No exception is thrown. The catch block is skipped.
A
C
Question 4
Easy
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        throw "Ravi";
    } catch (const char* name) {
        cout << "Hello, " << name << endl;
    }
    return 0;
}
String literals are of type const char*. The catch matches this type.
Hello, Ravi
Question 5
Medium
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
int safeDivide(int a, int b) {
    if (b == 0) throw domain_error("Division by zero");
    return a / b;
}
int main() {
    try {
        cout << safeDivide(10, 2) << endl;
        cout << safeDivide(10, 0) << endl;
    } catch (const domain_error& e) {
        cout << e.what() << endl;
    }
    try {
        cout << safeDivide(15, 3) << endl;
    } catch (const domain_error& e) {
        cout << e.what() << endl;
    }
    return 0;
}
First try: 10/2 succeeds, 10/0 throws. Second try: 15/3 succeeds.
5
Division by zero
5
Question 6
Medium
What is the output?
#include <iostream>
using namespace std;
int main() {
    try {
        try {
            throw 10;
        } catch (int e) {
            cout << "Inner: " << e << endl;
            throw (e + 5);  // Throw new exception
        }
    } catch (int e) {
        cout << "Outer: " << e << endl;
    }
    return 0;
}
throw (e + 5) throws a NEW exception with value 15, not a rethrow.
Inner: 10
Outer: 15
Question 7
Medium
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
void level2() {
    throw runtime_error("Deep error");
}
void level1() {
    level2();
}
int main() {
    try {
        level1();
    } catch (const exception& e) {
        cout << e.what() << endl;
    }
    return 0;
}
The exception thrown in level2 propagates through level1 (which has no catch) to main.
Deep error
Question 8
Hard
What is the output?
#include <iostream>
using namespace std;
class Resource {
    int id;
public:
    Resource(int i) : id(i) { cout << "R" << id << "+ "; }
    ~Resource() { cout << "R" << id << "- "; }
};
void func() {
    Resource r1(1), r2(2);
    throw 0;
    Resource r3(3);
}
int main() {
    try {
        Resource r0(0);
        func();
    } catch (int) {
        cout << "caught "; 
    }
    cout << "end" << endl;
    return 0;
}
r3 is never created (after throw). r2 and r1 are destroyed during unwinding, then r0.
R0+ R1+ R2+ R2- R1- R0- caught end
Question 9
Hard
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
class AppError : public runtime_error {
public:
    AppError(const string& msg) : runtime_error(msg) {}
};
void step3() { throw AppError("Step 3 failed"); }
void step2() {
    try { step3(); }
    catch (const AppError& e) {
        cout << "step2: " << e.what() << endl;
        throw;  // Rethrow
    }
}
void step1() {
    try { step2(); }
    catch (const runtime_error& e) {
        cout << "step1: " << e.what() << endl;
    }
}
int main() {
    step1();
    cout << "Done" << endl;
    return 0;
}
step3 throws AppError. step2 catches, logs, rethrows. step1 catches as runtime_error.
step2: Step 3 failed
step1: Step 3 failed
Done
Question 10
Hard
What is the output?
#include <iostream>
#include <stdexcept>
using namespace std;
int convert(const string& s) {
    try {
        int val = stoi(s);
        if (val < 0) throw domain_error("Negative");
        return val;
    } catch (const invalid_argument&) {
        throw runtime_error("Not a number: " + s);
    }
}
int main() {
    string inputs[] = {"42", "abc", "-5"};
    for (int i = 0; i < 3; i++) {
        try {
            cout << convert(inputs[i]) << endl;
        } catch (const domain_error& e) {
            cout << "Domain: " << e.what() << endl;
        } catch (const runtime_error& e) {
            cout << "Runtime: " << e.what() << endl;
        }
    }
    return 0;
}
"42" succeeds. "abc" makes stoi throw invalid_argument, which is caught and rethrown as runtime_error. "-5" throws domain_error.
42
Runtime: Not a number: abc
Domain: Negative

Multiple Choice Questions

MCQ 1
Which keyword is used to throw an exception in C++?
  • A. raise
  • B. throw
  • C. error
  • D. except
Answer: B
B is correct. C++ uses throw to create and throw exceptions. Python uses raise (A). error and except are not C++ keywords.
MCQ 2
What happens if an exception is thrown but no catch block matches?
  • A. The program continues normally
  • B. The exception is silently ignored
  • C. std::terminate() is called and the program crashes
  • D. The program restarts
Answer: C
C is correct. If no matching catch block is found, the runtime calls std::terminate(), which by default calls std::abort(), crashing the program.
MCQ 3
What does catch(...) do?
  • A. Catches only integer exceptions
  • B. Catches exceptions of any type
  • C. Catches no exceptions
  • D. Causes a compilation error
Answer: B
B is correct. The catch(...) handler (catch-all) catches exceptions of any type. It is used as a last resort when you want to handle any exception regardless of type.
MCQ 4
Which header provides the standard exception classes like runtime_error and logic_error?
  • A. <exception>
  • B. <stdexcept>
  • C. <error>
  • D. <throw>
Answer: B
B is correct. <stdexcept> provides runtime_error, logic_error, invalid_argument, out_of_range, etc. <exception> provides only the base std::exception class.
MCQ 5
What method does std::exception provide to get the error message?
  • A. message()
  • B. what()
  • C. error()
  • D. toString()
Answer: B
B is correct. std::exception and its subclasses provide the what() virtual method, which returns a const char* description of the error.
MCQ 6
What is stack unwinding in the context of exceptions?
  • A. Clearing the heap memory
  • B. The process of destroying local objects in each stack frame between throw and catch
  • C. Resizing the call stack
  • D. Printing the stack trace
Answer: B
B is correct. Stack unwinding is the automatic destruction of local objects (calling their destructors) in each stack frame as the runtime searches for a matching catch block. This ensures RAII resources are properly released.
MCQ 7
Why should catch blocks be ordered from most specific to most general?
  • A. For better performance
  • B. Because catch blocks are tried in order, and a general base class catch would match before a specific derived class catch
  • C. To avoid compilation errors
  • D. Because the last catch is always executed
Answer: B
B is correct. Catch blocks are tried in order. If catch(const exception&) comes before catch(const out_of_range&), the base class handler catches everything and the specific handler is unreachable.
MCQ 8
What is the difference between throw; and throw e; inside a catch block?
  • A. They are identical
  • B. throw; rethrows the original exception preserving its type; throw e; creates a copy that may be sliced
  • C. throw; throws a new exception; throw e; rethrows the original
  • D. throw; is invalid syntax
Answer: B
B is correct. throw; rethrows the current exception, preserving its original dynamic type. throw e; creates a new exception from the caught reference, potentially slicing a derived exception to the base type.
MCQ 9
What does the noexcept specifier do?
  • A. Makes the function throw a specific exception
  • B. Declares that the function will not throw any exceptions
  • C. Catches all exceptions inside the function
  • D. Disables exception handling for the entire program
Answer: B
B is correct. noexcept declares that a function will not throw. If a noexcept function throws, std::terminate() is called. It enables compiler optimizations and is important for STL move operations.
MCQ 10
What are the three levels of exception safety guarantee?
  • A. Low, medium, high
  • B. No-throw, strong (commit-or-rollback), basic (valid state, no leaks)
  • C. Compile-time, runtime, link-time
  • D. Static, dynamic, hybrid
Answer: B
B is correct. No-throw: function never throws (noexcept). Strong: if exception occurs, state rolls back completely. Basic: if exception occurs, program is in a valid state with no resource leaks.
MCQ 11
Why should destructors never throw exceptions?
  • A. Destructors cannot contain throw statements
  • B. If a destructor throws during stack unwinding from another exception, std::terminate() is called
  • C. Destructors are never called during exceptions
  • D. Throwing from destructors is only a warning
Answer: B
B is correct. If a destructor throws while the stack is unwinding from another exception (two active exceptions), C++ calls std::terminate(). Destructors should be noexcept and handle errors internally.
MCQ 12
What is RAII and how does it relate to exceptions?
  • A. A design pattern for avoiding exceptions entirely
  • B. Resource Acquisition Is Initialization: resources are tied to object lifetime, and stack unwinding during exceptions automatically calls destructors to release resources
  • C. A compiler optimization for exception handling
  • D. A way to convert exceptions to return codes
Answer: B
B is correct. RAII ensures that resources (memory, files, locks) are released by destructors. Since stack unwinding during exceptions calls destructors of all local objects, RAII guarantees no resource leaks even when exceptions occur. This is why C++ does not need finally blocks.
MCQ 13
Can you throw any type as an exception in C++?
  • A. Only std::exception subclasses
  • B. Only strings
  • C. Yes, any type including int, string, or custom classes
  • D. Only pointers
Answer: C
C is correct. C++ allows throwing any type: integers, strings, C-strings, custom classes, or standard exception classes. However, best practice is to throw classes derived from std::exception.
MCQ 14
What is the base class of all standard exception classes in C++?
  • A. runtime_error
  • B. logic_error
  • C. exception
  • D. error
Answer: C
C is correct. std::exception (from <exception>) is the base class. logic_error and runtime_error derive from it, and more specific exceptions like invalid_argument derive from those.
MCQ 15
What is the strong exception safety guarantee?
  • A. The function never throws
  • B. If an exception is thrown, the state is rolled back to before the function was called
  • C. If an exception is thrown, the state is valid but may be changed
  • D. All exceptions are caught internally
Answer: B
B is correct. The strong guarantee (commit-or-rollback) means either the operation succeeds completely, or if it fails, the state is exactly as it was before the call. The copy-and-swap idiom is commonly used to achieve this.
MCQ 16
Does C++ have a finally block like Java?
  • A. Yes, it works the same way
  • B. No, C++ uses RAII and destructors instead of finally
  • C. Yes, but only in C++20
  • D. No, C++ does not support cleanup during exceptions
Answer: B
B is correct. C++ does not have finally. Instead, it relies on RAII: destructors of local objects automatically run during stack unwinding. This is considered more robust than finally because cleanup happens deterministically without explicit blocks.
MCQ 17
What happens if a noexcept function throws an exception?
  • A. The exception is silently ignored
  • B. The exception propagates normally
  • C. std::terminate() is called immediately
  • D. The function returns a default value
Answer: C
C is correct. If a function declared noexcept throws an exception, the runtime calls std::terminate() immediately. The exception does not propagate, and stack unwinding may or may not occur (implementation-defined).
MCQ 18
Why do STL containers prefer noexcept move constructors?
  • A. For readability
  • B. Because if a move constructor throws during reallocation, the container cannot recover to a valid state
  • C. To reduce binary size
  • D. STL does not care about noexcept
Answer: B
B is correct. During reallocation (e.g., vector resize), if a move constructor throws after some elements are moved, the container cannot roll back. With noexcept move, the container knows the operation is safe and uses move instead of copy.
MCQ 19
What does the what() method of std::exception return?
  • A. An integer error code
  • B. A const char* description of the error
  • C. A boolean indicating success
  • D. The exception type name
Answer: B
B is correct. The what() virtual method returns a const char* string that describes the error. Custom exception classes typically override this to provide meaningful error messages.
MCQ 20
What is exception-neutral code?
  • A. Code that never throws exceptions
  • B. Code that catches all exceptions
  • C. Code that does not catch exceptions but still provides basic exception safety by allowing exceptions to propagate cleanly
  • D. Code that converts exceptions to error codes
Answer: C
C is correct. Exception-neutral code does not handle exceptions itself but allows them to propagate. It still provides the basic guarantee (valid state, no resource leaks) through RAII and proper cleanup, even though it does not catch exceptions.

Coding Challenges

Challenge 1: Safe Division with Exception Handling

Easy
Write a function safeDivide(int a, int b) that throws a domain_error if b is 0. In main, call it multiple times with different inputs and handle the exception. Show that the program continues after catching the exception.
Sample Input
safeDivide(10, 2), safeDivide(10, 0), safeDivide(15, 3)
Sample Output
10 / 2 = 5 Error: Division by zero 15 / 3 = 5
Use domain_error from <stdexcept>. Catch by const reference. Show continuation after catch.
#include <iostream>
#include <stdexcept>
using namespace std;

int safeDivide(int a, int b) {
    if (b == 0) throw domain_error("Division by zero");
    return a / b;
}

int main() {
    int pairs[][2] = {{10, 2}, {10, 0}, {15, 3}};
    for (int i = 0; i < 3; i++) {
        try {
            int result = safeDivide(pairs[i][0], pairs[i][1]);
            cout << pairs[i][0] << " / " << pairs[i][1]
                 << " = " << result << endl;
        } catch (const domain_error& e) {
            cout << "Error: " << e.what() << endl;
        }
    }
    return 0;
}

Challenge 2: Custom Exception Hierarchy for Banking

Medium
Create a BankException hierarchy: BankException (base), InsufficientFunds (with amount and balance), InvalidAmount (with amount). Create a BankAccount class with deposit() and withdraw() that throw appropriate exceptions. Demonstrate catching specific and general exceptions.
Sample Input
Account balance: 5000. Deposit -100. Withdraw 3000. Withdraw 5000.
Sample Output
Balance: 5000 InvalidAmount: Cannot deposit -100 Withdrew 3000. Balance: 2000 InsufficientFunds: Cannot withdraw 5000. Balance: 2000
Custom exceptions inheriting from runtime_error. Include context data (amount, balance). Catch specific before general.
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

class BankException : public runtime_error {
public:
    BankException(const string& msg) : runtime_error(msg) {}
};

class InsufficientFunds : public BankException {
    double amount, balance;
public:
    InsufficientFunds(double a, double b)
        : BankException("Cannot withdraw " + to_string((int)a)
          + ". Balance: " + to_string((int)b)),
          amount(a), balance(b) {}
};

class InvalidAmount : public BankException {
    double amount;
public:
    InvalidAmount(const string& op, double a)
        : BankException("Cannot " + op + " " + to_string((int)a)),
          amount(a) {}
};

class BankAccount {
    double balance;
public:
    BankAccount(double b) : balance(b) {}
    double getBalance() const { return balance; }
    void deposit(double amt) {
        if (amt <= 0) throw InvalidAmount("deposit", amt);
        balance += amt;
    }
    void withdraw(double amt) {
        if (amt <= 0) throw InvalidAmount("withdraw", amt);
        if (amt > balance) throw InsufficientFunds(amt, balance);
        balance -= amt;
    }
};

int main() {
    BankAccount acc(5000);
    cout << "Balance: " << acc.getBalance() << endl;

    try { acc.deposit(-100); }
    catch (const InvalidAmount& e) { cout << "InvalidAmount: " << e.what() << endl; }

    try {
        acc.withdraw(3000);
        cout << "Withdrew 3000. Balance: " << acc.getBalance() << endl;
    } catch (const BankException& e) { cout << e.what() << endl; }

    try { acc.withdraw(5000); }
    catch (const InsufficientFunds& e) { cout << "InsufficientFunds: " << e.what() << endl; }

    return 0;
}

Challenge 3: RAII Resource Manager with Stack Unwinding

Medium
Create a ResourceManager RAII class that acquires a named resource in the constructor and releases it in the destructor. Create a chain of three functions, each creating a ResourceManager. The deepest function throws an exception. Demonstrate that all resources are released during stack unwinding.
Sample Input
Resources: Database, FileSystem, Network. Exception at Network level.
Sample Output
Acquired: Database Acquired: FileSystem Acquired: Network Released: Network Released: FileSystem Released: Database Handled: Network failure
RAII pattern. Show destruction order during unwinding. All resources must be released.
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

class ResourceManager {
    string name;
public:
    ResourceManager(const string& n) : name(n) {
        cout << "Acquired: " << name << endl;
    }
    ~ResourceManager() {
        cout << "Released: " << name << endl;
    }
};

void networkLayer() {
    ResourceManager r("Network");
    throw runtime_error("Network failure");
}

void fileLayer() {
    ResourceManager r("FileSystem");
    networkLayer();
}

void dbLayer() {
    ResourceManager r("Database");
    fileLayer();
}

int main() {
    try {
        dbLayer();
    } catch (const exception& e) {
        cout << "Handled: " << e.what() << endl;
    }
    return 0;
}

Challenge 4: Type-Safe Configuration Parser with Exceptions

Hard
Create a Config class that stores key-value pairs (string -> string). Provide get(key) that throws KeyNotFound if the key does not exist, and getInt(key) that throws ParseError if the value cannot be converted to int. Both custom exceptions should inherit from ConfigError (which inherits from runtime_error). Demonstrate with valid and invalid lookups.
Sample Input
Config: {"port": "8080", "host": "localhost", "debug": "yes"}. Get port as int, host, missing key, debug as int.
Sample Output
port = 8080 host = localhost KeyNotFound: Key 'timeout' not found ParseError: Cannot parse 'yes' as int for key 'debug'
Custom exception hierarchy. Include key name in error messages. Use stoi for conversion.
#include <iostream>
#include <stdexcept>
#include <string>
#include <map>
using namespace std;

class ConfigError : public runtime_error {
public:
    ConfigError(const string& msg) : runtime_error(msg) {}
};

class KeyNotFound : public ConfigError {
public:
    KeyNotFound(const string& key)
        : ConfigError("Key '" + key + "' not found") {}
};

class ParseError : public ConfigError {
public:
    ParseError(const string& key, const string& val)
        : ConfigError("Cannot parse '" + val + "' as int for key '" + key + "'") {}
};

class Config {
    map<string, string> data;
public:
    void set(const string& key, const string& val) { data[key] = val; }

    string get(const string& key) const {
        auto it = data.find(key);
        if (it == data.end()) throw KeyNotFound(key);
        return it->second;
    }

    int getInt(const string& key) const {
        string val = get(key);
        try {
            return stoi(val);
        } catch (const invalid_argument&) {
            throw ParseError(key, val);
        }
    }
};

int main() {
    Config cfg;
    cfg.set("port", "8080");
    cfg.set("host", "localhost");
    cfg.set("debug", "yes");

    try { cout << "port = " << cfg.getInt("port") << endl; }
    catch (const ConfigError& e) { cout << e.what() << endl; }

    try { cout << "host = " << cfg.get("host") << endl; }
    catch (const ConfigError& e) { cout << e.what() << endl; }

    try { cout << cfg.get("timeout") << endl; }
    catch (const KeyNotFound& e) { cout << "KeyNotFound: " << e.what() << endl; }

    try { cout << cfg.getInt("debug") << endl; }
    catch (const ParseError& e) { cout << "ParseError: " << e.what() << endl; }

    return 0;
}

Challenge 5: Transaction System with Strong Exception Safety

Hard
Create a simple Ledger class that maintains a list of transactions (amounts). Implement transfer(from_ledger, to_ledger, amount) that provides the strong exception safety guarantee: if the deposit to the destination fails (e.g., exceeds a max balance), the withdrawal from the source is rolled back. Demonstrate a successful and a failed transfer.
Sample Input
Ledger A: balance 1000, max 5000. Ledger B: balance 4500, max 5000. Transfer 300 from A to B (fails: B would exceed max). Transfer 300 from A to C (balance 0, max 5000, succeeds).
Sample Output
A=1000, B=4500, C=0 Transfer 300 A->B failed: Exceeds max balance A=1000, B=4500, C=0 Transfer 300 A->C succeeded A=700, B=4500, C=300
Strong exception safety: failed transfer must leave both ledgers unchanged. Use RAII or rollback pattern.
#include <iostream>
#include <stdexcept>
using namespace std;

class Ledger {
    string name;
    double balance;
    double maxBalance;
public:
    Ledger(string n, double b, double mx) : name(n), balance(b), maxBalance(mx) {}
    string getName() const { return name; }
    double getBalance() const { return balance; }

    void withdraw(double amt) {
        if (amt > balance) throw runtime_error("Insufficient balance");
        balance -= amt;
    }

    void deposit(double amt) {
        if (balance + amt > maxBalance)
            throw runtime_error("Exceeds max balance");
        balance += amt;
    }
};

void transfer(Ledger& from, Ledger& to, double amount) {
    from.withdraw(amount);  // Step 1
    try {
        to.deposit(amount);  // Step 2
    } catch (...) {
        from.deposit(amount);  // Rollback step 1
        throw;  // Rethrow
    }
}

void showAll(Ledger& a, Ledger& b, Ledger& c) {
    cout << a.getName() << "=" << a.getBalance()
         << ", " << b.getName() << "=" << b.getBalance()
         << ", " << c.getName() << "=" << c.getBalance() << endl;
}

int main() {
    Ledger a("A", 1000, 5000);
    Ledger b("B", 4500, 5000);
    Ledger c("C", 0, 5000);
    showAll(a, b, c);

    try {
        transfer(a, b, 300);
        cout << "Transfer 300 A->B succeeded" << endl;
    } catch (const exception& e) {
        cout << "Transfer 300 A->B failed: " << e.what() << endl;
    }
    showAll(a, b, c);

    try {
        transfer(a, c, 300);
        cout << "Transfer 300 A->C succeeded" << endl;
    } catch (const exception& e) {
        cout << "Transfer 300 A->C failed: " << e.what() << endl;
    }
    showAll(a, b, c);
    return 0;
}

Need to Review the Concepts?

Go back to the detailed notes for this chapter.

Read Chapter Notes

Want to learn C++ with a live mentor?

Explore our C++ Masterclass