4 hours · 5 marks
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 — a block containing code that might throw an exception.throw — signals that an exceptional condition has occurred, passing an exception object.catch — a block that receives and handles a thrown exception of a matching type.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();
}
catch block can handle errors from many statements/function calls within a try block, avoiding repetitive if-checks after every call.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.
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";
}
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";
}
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();
}
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;
}
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;).
std::terminate() immediately, since C++ cannot handle two simultaneous 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).
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.
try, catch, and throw with a program. 6 markstry wraps risky code, throw signals an error, catch handles it. See the division-by-zero example in section 9.1.#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;
}
}
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.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.std::terminate() is called immediately and the program aborts, since C++ cannot manage two simultaneous exceptions. See section 9.9.catch(...)? 3 markscatch(...) 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.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.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.