Study guide
Exception Handling in C++: try, catch, throw, Rethrow with Traced Examples and new/delete
Exception handling in C++ for CSC166 and BIT153: a traced try, catch(...) and rethrow program with output 2, the plain int variant, catching by reference, nested try, bad_alloc and new/delete.
2026-10-24 | notesharu team
Exception handling in C++ is examined as theory (explain try, catch and throw) and as a trace question: a short program with several functions that throw and rethrow, and you must give the output. This article works through exactly such a program, the one with a class holding an int v, a function that throws, one that catches by reference and throws a new object with v + 1, one that uses catch(...) and rethrows, and a main that prints i.v. It belongs to the Function Templates and Exception Handling unit of TU BSc CSIT Object Oriented Programming (CSC166, unit 7) and the Exception Handling and File I/O unit of BIT153 (unit 10).
It also covers exception handling in C++ in general: syntax, catching by reference vs value, catch(...), rethrow with a bare throw;, nested try blocks, exception specifications, and the new and delete keywords in C++ with bad_alloc.
The program, corrected so that it compiles
The version students see often has a class named int, which is not legal because int is a keyword. Rename it Num. Here is the compilable program where a() throws Num(1).
#include <iostream>
using namespace std;
class Num {
public:
int v;
Num(int x) : v(x) {}
};
void a() {
throw Num(1);
}
void b() {
try {
a();
}
catch (Num &i) {
throw Num(i.v + 1);
}
}
void c() {
try {
b();
}
catch (...) {
throw;
}
}
int main() {
try {
c();
}
catch (Num &i) {
cout << i.v;
}
return 0;
}
Tracing the output
The output is 2. The trace goes as follows.
main calls c() inside a try block. c() calls b() inside its own try block. b() calls a() inside its try block. a() executes throw Num(1), which creates an exception object of type Num with v = 1. The function a() has no handler, so the stack unwinds back to its caller, b().
In b(), the throw happened inside the try block, so the handlers are checked. catch (Num &i) matches because the thrown type is Num. The reference i binds to the exception object, and the handler executes throw Num(i.v + 1), which is Num(2). This is a new exception thrown from inside a handler. The old object is destroyed, and the new one propagates out of b() because there is no enclosing try inside b() around this handler.
In c(), the call to b() is inside a try block. The thrown type is Num, and catch (...) matches anything. The handler has a bare throw;, which rethrows the exception currently being handled, the same object Num(2), without copying it. It leaves c().
In main, catch (Num &i) matches, and i.v is 2, so the program prints 2.
The variant where a() throws a plain int
A second version of the question changes a() to throw 0; while leaving everything else the same. The thrown value has type int, not Num. Now the trace changes completely.
In b(), catch (Num &i) does not match, because C++ does no implicit conversion between an int and a user class in handler matching. Matching is by type (exact, or a base class, or a few standard pointer and reference conversions), not by whether a constructor could build a Num from the value. So b() does not handle it, and the exception passes out of b().
In c(), catch (...) matches and rethrows with throw;, so the same int leaves c(). In main, catch (Num &i) again does not match an int. No handler is found anywhere, so std::terminate() is called, which by default calls abort(). The program ends abnormally, nothing is printed by cout, and a typical compiler's runtime writes a message such as "terminate called after throwing an instance of 'int'" to the error stream. Whether the stack is unwound before terminate in this case is implementation defined, so do not rely on destructors running. The fix is to add a handler catch (int) or catch (...) in main.
try, catch and throw syntax
Exception handling separates error detection from error handling. Code that may fail is placed in a try block. Code that detects the problem executes throw expression;. The handlers follow the try block as catch (type name) { ... } clauses.
try {
if (b == 0) throw "division by zero";
cout << a / b;
}
catch (const char *msg) {
cout << msg;
}
catch (...) {
cout << "unknown error";
}
Handlers are tried in order, and the first matching one runs, so put specific types before general ones and catch (...) last. After a handler finishes, execution continues after the last handler, not after the throw. A throw with no matching handler anywhere ends in terminate().
Catch by reference vs by value
catch (Num x) copies the exception object into x. This costs a copy, and for class hierarchies it causes slicing: if a Derived object is thrown and the handler takes Base by value, only the Base part is copied and virtual behaviour is lost. catch (Num &x) binds to the thrown object itself, with no copy and no slicing, so polymorphism works. The usual advice is to throw by value and catch by (const) reference. A reference handler can also modify the exception object, which would be visible if it is rethrown with throw;.
catch(...) and rethrowing
catch (...) is the catch-all handler. It cannot name the exception, so it cannot read its value, which is why it is typically used to do cleanup and then rethrow with a bare throw;. The bare throw; has no operand and is valid only inside a handler. It rethrows the same exception object, preserving its dynamic type. Contrast this with throw i; inside catch (Base &i), which throws a fresh copy of the static type Base, causing slicing.
Nested try blocks
A try block can contain another try block, and a handler can be placed inside a handler. When an exception is thrown, the inner handlers are checked first. If none matches, it propagates to the enclosing try. This is how the stack unwinding in the trace above works: each function's handlers are checked, then the call stack is popped. During unwinding, destructors of local objects run, which is why RAII (resources released in destructors) is the safe way to manage resources in the presence of exceptions.
Exception specifications
Older C++ allowed a function to declare which exceptions it may throw, for example void f() throw(int, Num); and void g() throw(); for none. If a function threw something outside its list, std::unexpected() was called. Dynamic specifications of this form were deprecated and removed in C++17. Modern code uses noexcept to promise that a function does not throw. Your syllabus may still ask for the older form, so know both.
new, delete and bad_alloc
The new operator allocates memory on the heap and returns a pointer, calling the constructor for objects. delete calls the destructor and frees the memory, and delete[] frees an array created by new[]. If new cannot find enough memory it throws std::bad_alloc (declared in <new>).
#include <iostream>
#include <new>
using namespace std;
int main() {
try {
int *p = new int[1ULL << 60];
delete[] p;
}
catch (bad_alloc &e) {
cout << "Allocation failed: " << e.what();
}
return 0;
}
The form new (nothrow) int[n] instead returns a null pointer on failure, so the caller must test it. Every new must be matched by one delete, and forgetting causes a memory leak.
How to write it in the exam
For a trace question, write the call chain, say which handler matches at each level and why, and state the final output. Show that a bare throw; rethrows the same object. For a theory question on exception handling, define it, give the try, catch and throw syntax with one small example, explain the order of handlers, and mention catch-all and rethrow.
The unit notes are on Function Templates and Exception Handling in CSC166, with the CSC166 subject page for past papers. BIT students should read Exception Handling and File I/O in BIT153 and the pointer and memory unit Pointers, References and Memory Management.
Put it into practice
Browse all subjectsMore articles
Study guide
2D Geometric Transformation in Computer Graphics: Matrices and a Solved Numerical
2026-10-24
Study guide
6 Advantages of Cloud Computing, Availability and Programming Models: Exam Guide
2026-10-24
Study guide
Common Bus System in Computer Architecture with Diagram: Registers, Selection Table, Answer
2026-10-24
Study guide
Data Transfer Instructions in Computer Architecture: Mnemonic Tables, Types and Example
2026-10-24
Study guide
Definition of Constitution by Different Scholars and Government Power Distribution in Nepal 2015
2026-10-24
Study guide
Clocked RS Flip Flop Truth Table and Mod 3, Mod 5 Synchronous Counter Design: Digital Logic
2026-10-24