核心要点速览
- 异常传播:无匹配
catch时,异常逐层向上传递至调用者,直至捕获或触发std::terminate() - 栈展开:传播过程中自动逆序销毁局部对象(按构造逆序),保障资源回收
- 核心规则:析构函数禁止抛异常(避免双重异常),未完全构造对象不执行析构
- 异常安全:依赖栈展开原子性 + RAII 机制,避免资源泄漏
- 补充要点:异常匹配优先精确 / 继承兼容,捕获
const引用可避免异常切片
一、异常传播:从抛出点到捕获点的传递逻辑
概念
函数抛出异常且自身无匹配try-catch时,异常会沿函数调用栈逐层向上传播,依次遍历调用者上下文,直到被某个catch块捕获;若全程无捕获,程序调用std::terminate()终止(默认执行std::abort())。
传播流程
- 函数调用链:A→B→C,C 中抛出异常;
- C 无
catch匹配,异常传播至 B; - B 无
catch匹配,继续传播至 A; - A 的
try块监控到异常,匹配catch执行处理; - 若 A 仍无匹配,异常传播至
main函数,最终未捕获则程序终止。
细节
- 异常传播时,被跳过函数中 “已执行但未进入
try块” 的代码不再执行; - 传播仅终止当前线程执行流,不跨线程影响其他线程;
std::terminate()可通过std::set_terminate()自定义,但无法恢复程序执行,不推荐使用;- 异常匹配遵循 “精确匹配优先、派生类兼容基类匹配”,捕获值会导致 “异常切片”(丢失派生类信息),推荐捕获
const引用; - 异常对象存储于专用内存区域,传播过程中仅拷贝或引用,处理完成后自动销毁。
二、栈展开:异常传播时的资源回收机制
概念
异常传播过程中,编译器自动触发栈展开:从异常抛出点开始,逆序遍历函数调用栈,销毁 “try块到抛出点之间” 的所有局部对象(自定义对象、智能指针等),确保资源(内存、文件句柄、锁)被正确释放。
特性
- 逆序销毁:严格遵循 “后构造先销毁”,与正常函数退出时的对象销毁顺序一致;
- 原子性:局部对象析构强制连续执行,不会因中间错误中断;
- 无额外开销:编译器自动实现,无需手动编写回收代码;
- 终止条件:异常被
catch块捕获后,栈展开立即停止,程序跳转至catch块后继续执行。
代码示例(直观理解逆序销毁)
class ResourceGuard {
public:
ResourceGuard(const std::string& name) : resName(name) {}
~ResourceGuard() { std::cout << "释放资源:" << resName << std::endl; }
private:
std::string resName;
};
void riskyFunc() {
ResourceGuard res1("文件句柄");
ResourceGuard res2("内存块");
throw std::out_of_range("索引越界"); // 触发栈展开
}
// 栈展开时输出:释放资源:内存块 → 释放资源:文件句柄(逆序销毁)
三、关键规则与风险规避
1. 析构函数禁止抛出异常
- 风险:栈展开过程中,析构函数抛新异常会导致 “双重异常”,程序直接终止;
- 规则:C++11 后析构函数默认隐式
noexcept,显式声明更清晰;内部错误需自行处理(如记录日志),不向外抛出。
正确示例
class SafeGuard {
public:
~SafeGuard() noexcept { // 显式声明noexcept
try { releaseResource(); }
catch(...) { std::cerr << "资源释放失败" << std::endl; } // 内部处理错误
}
};
2. 未完全构造的对象,析构函数不执行
- 规则:构造函数抛出异常时,对象视为 “未完全构造”,其析构函数不会被调用;
- 风险:构造函数中分配的资源(如
new内存、文件句柄)会泄漏; - 解决方案:用 RAII 机制管理资源(智能指针、资源守卫类),确保构造失败时资源自动释放。
3. 栈展开不销毁动态分配对象
- 规则:栈展开仅销毁栈上局部对象,堆上动态分配的对象(
new/malloc创建)不会自动释放; - 规避方案:优先用
unique_ptr/shared_ptr管理动态内存,利用 RAII 机制在栈展开时自动释放堆内存。
补充
- 异常传播与函数调用栈:传播路径即调用栈回溯路径,栈展开仅覆盖 “
try块到抛出点” 的栈帧; - 栈展开性能开销:主要来自局部对象析构调用,性能影响可忽略,远低于资源泄漏的风险;
- 异常安全等级:最高为 “强异常安全”(异常后程序状态恢复如初),依赖栈展开 + RAII 机制实现;
- 多线程异常传播:线程内异常仅在当前线程传播,未捕获会导致当前线程终止,不影响进程(除非主线程);
- 异常重新抛出:
catch块中用throw;可保留原异常完整信息,避免throw 异常对象;导致的信息丢失。

被折叠的 条评论
为什么被折叠?



