Week13 Exception
异常(Exception)指的是程序在运行时遇到无法正常处理的问题时的一种机制。
其核心思想在于,当问题发生时,当前代码可能不知道如何解决,但它知道不能继续正常执行。异常机制允许程序暂停当前操作,将问题“抛”给能够处理它的代码。
首先需要强调的是,异常是运行时机制(针对run-time error),在程序运行时触发,用于处理运行时错误(比如文件无法打开、内存分配失败、数组越界等),C++编译器不会在编译时检查异常是否会发生,而是将异常处理逻辑留到运行时执行。
抛出异常throw
¶
throw
关键字用于抛出异常对象,暂停当前代码执行,并将控制权传递给调用栈中能够处理该异常的catch
块,throw
的使用有以下几点规则:
- 抛出任意类型:
throw
可以抛出任何类型的对象(包括基本类型、自定义类、标准库异常等等),但是我们推荐使用自定义异常类或std::exception
的派生类 - 抛出后中断:
throw
执行后,当前函数立即停止,控制权沿调用栈向上传播。 - 异常对象拷贝:抛出的对象会被复制(除非是通过引用捕获的),因此应确保对象支持拷贝或通过引用捕获
try
监控可能抛出异常的代码¶
try
块包裹可能抛出异常的代码,告诉C++运行时在这些代码中监视异常,其使用也有以下几点规则:
try
后面必须至少跟一个catch
块,否则编译器会报错- 只监控
try
块内的代码,块外代码不受影响 try
块会引入运行时开销
catch
捕获和处理异常¶
catch
块用于捕获特定类型的异常,并执行相应的错误处理逻辑,其有以下几点规则
- 按类型捕获:
catch
块指定捕获的异常类型,可以是具体类型、基类或...
(捕获所有异常) catch
块按声明顺序检查,优先匹配精确类型,然后尝试基类转换,最后匹配...
- 在
catch
块中可以使用throw
,重新抛出当前异常 - 推荐使用
catch(Type& e)
避免对象切片
异常对象¶
异常对象携带错误信息,能够帮助调用者了解问题原因
异常对象通常继承自std::exception
或其派生类,提供what()
方法返回错误描述;
标准库异常¶
标准库异常在使用的时候需要#include<stdexcept>
,常见的标准库异常包括:
std::bad_alloc
:内存分配失败std::out_of_range
:索引越界std::runtime_error
:通用运行时错误
栈展开(Stack Unwinding)¶
当异常抛出后,C++会沿调用栈向上寻找匹配的catch
块,在此过程中,自动销毁栈上对象(调用其析构函数),确保资源正确释放
栈展开的规则有如下几点:
- 自动清理:栈上对象的析构函数会在异常传播时自动调用
- 析构函数不抛异常:如果析构函数在栈展开期间抛出异常,会导致
std::terminate()