- 程序有时会遇到
运行阶段
错误,导致程序无法正常地运行下去- 异常为禅理这种意外情况提供了一种功能强大而灵活的工具
传统方法
abort
&exit
:
abort:
abort函数
的原型位于头文件cstdlib
中函数功能:
(1)向标准错误流cerr
发送消息abnormal program termination
(程序异常终止)
(2)终止程序的执行
(3)abort
是否刷新文件缓冲区取决于实现
(4)返回一个随实现而异的值,告诉操作系统,处理失败
exit:
exit
直接终止程序的执行,并退出- 刷新文件缓存区,但不显示消息
exit
函数需要传递一个整型参数,exit
会返回该值
double hmean(double a, double b)
{
if(a == -b)
{
std::abort(); //exit(10);
}
return 2.0*a*b/(a+b);
}
- 返回错误码:
- 除异常终止程序外,可以使用
函数的返回值
来指出问题- 另一种在某个地方存储返回条件的方法是使用一个
全局变量
异常机制
异常
是对程序运行过程中发生异常情况的一种响应
异常
提供了将控制权
从程序的一部分传递到另一部分的途径
- 异常处理的组成:
引发异常
,使用关键字throw
(1)throw
语句是跳转,即命令程序跳到另一条语句
(2)关键字throw
后的值(字符串
或对象
)指出了异常的特征使用异常处理程序捕获异常
,使用关键字catch
(1)catch
关键字表示捕获异常,处理程序以catch
开头
(2)关键字catch
后是位于括号中的类型参数,它指出了异常处理程序要响应的异常类型
(3)最后是用花括号
括起的代码块,指出要采取的措施
(4)关键字catch
和异常类型用作标签
,指出当异常被引发时,程序应跳到这个位置执行
(5)异常处理程序也被称为catch块
使用try块(特定的异常可能被激活的代码块)
,使用关键字try
(1)try块
后面跟着一个或多个catch块
(2)关键字try
的后面是一个由花括号
括起的代码块
,表明需要注意这些代码引发的异常
- 执行完
try块
中的语句后,若未引发任何异常,则程序跳过try块
后面的catch块
,继续执行catch
块后面的第一条语句- 执行
throw语句
类似于执行返回语句
(1)throw语句
也将终止函数的执行
(2)throw语句
不是将控制权返回给调用程序
,而是导致程序沿着函数调用序列后退
,直到找到包含try块
的函数- 若函数引发了异常,但没有
try块
或没有匹配的catch块
时,默认情况下,程序最终将调用abort
函数
- c++11支持的异常规范:
- 可以使用新增的关键字
noexcept
指出函数不会发生异常
- 未捕获异常:
- 当
throw
抛出异常,却没有try
块或没有匹配的catch块
时,则异常被称为未捕获异常
- 当出现
未捕获异常
时,默认情况下,会导致程序异常终止- 修改程序对未捕获异常的反应:
(1)未捕获异常不会导致程序立即异常终止
(2)程序会首先调用terminate()
,默认情况下,terminate()
会调用abort
函数
(3)可以通过调用set_terminate()
来修改terminate()
函数的行为
(4)set_terminate()
函数和terminate()
函数都是在头文件exception
中声明的
对象用作异常类型
引发异常
的函数传递一个对象
,可以使用不同的异常类型
来区分不同的函数
在不同的情况
下引发的异常,即确定异常的位置与类型- 对象可以携带信息,程序员可以根据这些信息来确定引发异常的原因,即确定发生异常的原因
- 使用省略号来表示异常类型,从而捕获任何类,并且将捕获所有异常的
catch块
放在最后面
- 引发异常时,编译器总是创建一个临时拷贝,即使
catch块
中指定的是引用基类引用
可以执行派生类对象
:假设有一组通过继承关联起来
的异常类型
,则在异常规范中只需列出一个基类引用
,它将与任何派生类对象
匹配- 如果一个异常类继承层次结构,应这样排列
catch
块:将捕获位于层次结构最下面的异常类的catch
语句放在最前面,将捕获基类异常的catch
语句放在最后面
栈解退
栈解退:
(1)函数由于出现异常(而不是由于返回)而终止,程序也将释放栈中的内存
(2)但不会再释放栈的第一个返回地址
后停止,而是继续释放栈,直到找到一个位于try块
中的返回地址
(3)随后,控制权
将转换到块尾的异常处理程序(catch块)
,而不是函数调用后面的第一条语句- 与
函数返回
相同,将会释放栈中的自动变量,若该自动变量为类对象,类的析构函数将被调用
(1)函数返回
仅仅处理函数
放在栈中的对象
(2)throw语句
则处理try块
和throw语句
之间整个函数调用序列放在栈中的对象
(3)若没有栈解退
这种特性,则引发异常
后,对于中间函数调用放在栈中的自动类对象,其析构函数将不会被调用
exception类
exception
头文件定义了exception
类,C++可以把它用作其他异常类的基类- 代码可以引发
exception
异常,也可以将exception
类用作基类exception
类中有一个名为what
的虚拟成员函数,它返回一个字符串,可以在从exception
派生而来的类中重新定义它
stdexcept
异常类:
stdexcept
是头文件,它定义了多个以公有方式
从exception
派生而来的类,如logic_error
类和runtime_error
类- 这些类的构造函数接受一个
string
对象作为参数,该参数提供了方法what()
以C-风格字串方式
返回的字符数据
logic_error
的派生类:
- 异常类系列
logic_error
描述了典型的逻辑错误domain_error:
函数在参数不再定义域时引发4.domain_error:
给函数传递了一个意料之外的值length_error:
指出没有足够空间来执行所需的操作out_of_bounds:
指示索引错误
runtime_error
的派生类:
runtime_error
描述了可能在运行期间发生但难以预计和防范的错误range_error:
计算结果可能不再函数允许的范围之内overflow_error:
上溢错误underflow_error:
上溢错误
bad_alloc
异常和new
:
- C++的最新处理方式是让
new
引发bad_alloc
异常bad_alloc
类是从exception
类公有派生而来的- C++标准提供了一种在失败时返回
空指针
的new
异常、类和继承
- 可以从一个异常类派生出另一个
- 可以在类定义中嵌套异常类声明来组合异常
- 这种嵌套声明本身可以被继承,还可以作为基类