栈展开(Stack Unwinding)机制详解与RAII
请解释 C++ 异常处理中的栈展开(stack unwinding)机制:抛出异常后如何逐层销毁局部对象?哪些情况下会跳过栈展开(如未捕获异常或 terminate)?RAII 为什么依赖栈展开?
回答
我还是少年
栈展开:当异常抛出时,运行时系统沿调用栈逐层回退,自动销毁每个栈帧中的局部对象(调用析构函数),直到找到匹配的 catch 子句。
过程:
- 异常对象在 throw 处构造(可能拷贝到特殊内存)。
- 从 throw 点向上展开栈帧,调用每个局部对象的析构函数(构造顺序的逆序)。
- 找到匹配的 catch 后,异常对象匹配到参数。
- 执行 catch 块,随后异常被释放。
跳过栈展开的情况:
- 异常未捕获 → 调用
std::terminate()(不展开)。 - 析构函数中抛出异常(且栈展开过程中析构函数再次抛出)→ terminate。
noexcept函数中抛出 → terminate(实现可能选择不展开或展开后 terminate)。
RAII 依赖:RAII 资源管理(智能指针、锁、文件句柄)在析构函数中释放资源。栈展开保证所有局部 RAII 对象的析构函数被调用,因此异常安全。若析构函数中都正确释放资源,则栈展开不会泄漏资源。