Exception Handling
Throwing Exceptions
Signal errors using the throw keyword, manage lifetime during stack unwinding, and understand rethrowing mechanics.
Interview: Stack unwinding behavior, destructor safety during unwinding, and the critical performance difference between throw; and throw e;.
The throw keyword is used to signal a runtime error. When an exception is thrown, the C++ runtime initiates Stack Unwinding, destroying all local automatic variables in the current scope and outer scopes until a suitable catch block is found.
Stack Unwinding
Automatically invokes destructors of local objects as execution exits stacked frames. This prevents leaks if RAII is used.
Rethrowing
Use a bare throw; statement inside a catch block to propagate the original exception object unchanged.
No-Double-Throw
If a destructor throws an exception during stack unwinding, std::terminate is called immediately.
The Rethrowing Dilemma: throw; vs throw e;
Inside a catch block, developers sometimes need to log the error and pass it up. The method used to rethrow is crucial:
throw;: Rethrows the original exception object. This preserves its dynamic type, allowing base catch blocks further up the stack to correctly capture the derived type.throw e;: Creates a new exception object of the static parameter type. This slices the exception, mutating the object to the base class and losing the original type information.
Code Walkthrough
This program demonstrates exception propagation, local object destruction, and correct rethrowing.
#include <iostream> #include <stdexcept>struct Resource { std::string name; Resource(std::string n) : name(n) { std::cout << name << " acquired\n"; } ~Resource() { std::cout << name << " destructed\n"; } };
void intermediateFunction() { Resource res("LocalResource"); // Properly cleaned up during stack unwinding throw std::runtime_error("Error in intermediateFunction"); }
int main() { try { try { intermediateFunction(); } catch (const std::exception& e) { std::cout << "Logging inside nested catch: " << e.what() << std::endl; throw; // Correct: Rethrows original std::runtime_error } } catch (const std::runtime_error& e) { std::cout << "Outer catch successfully caught: " << e.what() << std::endl; } return 0; }
Interview-Relevant Information
Q: What is the behavior of throw; compared to throw e; inside a catch(const BaseException& e) block?
Answer: throw; preserves the actual dynamic type of the exception object (even if it was a derived class like DerivedException). throw e; copies the parameter using the static type BaseException, causing slicing. Subsequent handlers will only see a BaseException object.
Q: Why should destructors never throw exceptions?
Answer: Destructors are automatically invoked during stack unwinding. If a destructor throws an exception while another exception is already active (propagating), the runtime has no safe way to handle both simultaneously. C++ rules dictate that this immediately terminates the application by calling std::terminate. Destructors are implicitly noexcept starting in C++11.
Quick Checklist
Are you using a bare throw; to rethrow caught exceptions? Do you avoid throwing in destructors? If yes, you are following C++ exception safety practices.
Use Cases
Propagating deep logic failures (e.g. invalid arguments, internal states) up to controller modules.
Aborting transaction sequences inside resource pipelines if prerequisites are unmet.
Common Mistakes
Throwing raw integers, characters, or strings (e.g. throw 404; or throw 'Error';), which bypass std::exception catching.
Throwing exceptions inside destructors, resulting in direct crashes during stack unwinding.