When an exception is thrown, the runtime unwinds the stack, destroying local objects in each function until a matching catch is found. Destructors run during unwinding. If no catch matches, the program terminates.
The exception propagates through every function on the call stack, giving each a chance to handle it. Stack unwinding ensures cleanup happens even when errors occur. Objects with proper destructors release their resources automatically during propagation.