章节回顾
异常处理提供了一种机制,将错误或异常情形的处理与常规控制流解耦。这使开发者能够在恰当的时间和位置灵活地处理错误,从而消除(或极大缓解)返回码所带来的混乱。
throw
语句用于抛出异常。try
块监视其内部或所调用代码抛出的异常。catch
块捕获匹配类型的异常并进行处理;默认情况下,被捕获的异常即视为已处理。- 异常被立即处理:一旦抛出,控制流跳转到最近的
try
块并寻找匹配的catch
;若找到,则回卷栈并继续执行;若找不到,程序调用std::terminate
并报告未处理异常。 - 任意数据类型(包括类)均可作为异常抛出。
catch
可指定类型捕获,亦可用省略号...
作为通配处理器。捕获基类引用的处理器也会捕获派生类异常。- 标准库抛出的所有异常均派生自
<exception>
头文件中的std::exception
;捕获std::exception&
即可捕获全部标准库异常,并通过what()
获取异常描述。 - 在
catch
块内可抛出新异常(不会被自身捕获),亦可使用无操作数的throw;
重新抛出原异常;切勿用捕获的异常变量再次抛出,否则会发生对象切片。 - 函数 try 块允许捕获整个函数体或成员初始化列表中抛出的异常,主要用于派生类构造函数。
- 绝不可在析构函数中抛出异常。
noexcept
说明符可用于声明函数的不抛/不失效保证。std::move_if_noexcept
在对象拥有noexcept
移动构造时返回可移动右值,否则返回可复制左值,以此在强异常保证前提下优先使用移动语义,否则退回复制。- 异常处理存在开销:通常使代码略慢,且异常触发时代价极高。异常应仅用于处理“异常”情形,而非常规错误(如输入无效)。
章节测验
编写一个 Fraction
类,其构造函数接受分子与分母。若用户传入 0 作为分母,抛出 std::runtime_error
异常(需包含头文件 <stdexcept>
)。在主程序中,提示用户输入两个整数。若分数有效,输出该分数;若无效,捕获 std::exception
并提示用户分母无效。
一次示例运行:
Enter the numerator: 5
Enter the denominator: 0
Invalid denominator