Exceptions 异常
抓取程序运行中的异常错误
将正常代码模块,和错误处理模块分开
Assert语句
- Debug vs Release
- Debug 模式 assert 语句生效 帮助开发者快速发现问题,提供详细的调试信息
- Release 模式 assert 语句被移除,不影响其他
抛出异常Exception
如何抛出
- 抛出一个对象
抛出给调用者,调用者如何处理?
如果啥也不写,默认是继续向上的caller传递异常
Catch
VectorIndexError
的类型的异常不怎么管这个异常,一个throw继续向上传
- 会catch所有类型
Catch的对象的匹配类型
- 严格匹配
- 能够向上构型
- catch(
...
)
举例第二种的向上构型的异常类型:
标准库已经有的Exception的类型Library
你可以自己继承这些类,然后自定义派生一些自己的Exception类
new的异常:bad_alloc()
在构造函数里面,抛出异常
- 如果编译器在构造函数中收到抛出的异常,析构函数将不会被调用:
当对象构造失败时(即构造函数抛出异常),该对象被认为从未完全构造完成,因此:
- 不会调用析构函数
- 只会析构已经完全构造好的成员对象
但是并不会释放已经动态分配的内存(所有这种情况有可能导致内存泄漏)
如何避免上述情况?——RAII
Using smart_pointers
将动态分配内存的管理,封装在一个smart_pointers的对象里面(其实就像vector),然后用这个对象来管理动态内存,相当于以栈管堆
这样,就算Ctor抛出了异常,也会递归调用已经构造的栈中的对象的析构,所以也会析构smart_pointers,就可以让你管理动态内存的释放,避免内存泄漏
在析构函数里面抛出异常
别这样写………..
在catch的时候一般都是By reference——用引用来获取异常(避免向上构型的时候的截断)
引用和指针一样,都可以在向上构型的时候避免被切片
Expectation 的向上传递的情况
让我通过具体例子来解释这种情况下的异常传递机制。
示例代码
执行流程
- 第二层未匹配时的行为:
- 堆栈展开(Stack Unwinding)过程:
- Level 3 抛出 MyException
- Level 2 的 catch 块无法匹配
- Level 2 函数的栈帧被展开
- 局部对象按照构造顺序的反序析构
- 资源被正确释放
- 异常继续向上传递
- Level 1 的 catch 块匹配成功并处理异常
详细说明
关键点
- 资源管理
- 所有自动变量(栈上对象)会被正确析构
- RAII模式确保资源释放
- 析构顺序与构造顺序相反
- 执行流程
- 未匹配的try-catch块会被跳过
- 函数中try-catch后的代码不会执行
- 继续向上查找匹配的catch块
- 异常安全性
- 现代C++推荐做法
总结:即使中间层(level2)没有匹配的catch块,异常处理机制也会确保:
- 所有资源被正确清理
- 所有局部对象被正确析构
- 异常继续向上传递直到找到匹配的处理程序
- 整个过程是安全和可预测的
这种机制保证了程序的健壮性和资源安全性,是C++异常处理系统的重要特性。
那么,Question:那么如果向上传递的所有的Caller都没有catch到这个异常,那么这一叠栈的Caller都会被析构?
是的,如果所有的调用栈
(call stack)
都没有捕获到异常,程序会继续展开所有栈帧直到 main
函数,如果 main 也没有捕获,程序会调用 std::terminate()
终止程序,跳出main