异常就是程序运行时出现的不正常,例如运行时耗尽了内存或遇到意外的非法输入. 异常存在于程序的正常功能之外,并要求程序立即处理. 异常机制提供程序中错误检测与错误处理部分之间的通信. c++的异常处理中包括:
throw表达式 : 错误检测部分使用这种表达式来说明遇到了不可处理的错误.可以说,throw引发了异常条件.
try块: 错误处理部分使用它来处理异常. try语句块以try关键字开始,并以一个或多个catch字句结束. 在try块中执行的代码所抛出的异常,通常会被其中一个catch字句处理.由于它们"处理"异常,catch字句也称处理代码.
由标准库定义的一组异常类,用来在throw和相应的catch之间传递有关的错误信息.
throw表达式
系统通过throw表达式抛出异常.throw表达式由关键字throw以及尾随的表达式组成, 通常以分号结束, 这样它就成为了表达式语句. throw表达式的类型决定了所抛出异常的类型.
以一个计算两个数的调和平均数的函数为例
double hmean ( double a, double b )
{
if ( a == -b )
{
std::cerr<<"untenable arguments to hmean()\n";
return ;
}
return 2.0*a*b/(a+b);
}
上面的代码中,如果a == -b;则立刻输出错误提示, 并结束程序. 该代码可以用throw抛出异常来改写,如下
double hmean ( double a, double b )
{
if ( a == -b )
throw runtime_error(" untenable arguments to hmean()");
return 2.0*a*b / (a+b);
}
throw语句使用了一个表达式,该表达式是runtime_error类型的对象.rumtime_error类型是标准库异常类中的一种,在stdexcept头文件中定义. 可以测试一下这个程序.
#include <stdexcept>
#include <iostream>
using namespace std;
int main()
{
double a,b,c;
while(1)
{
cout<<"输入a和b: ";
cin>>a>>b;
try {
c=hmean(a,b);
cout<<a<<"和"<<b<<"的调和平均数为: "<<c<<endl;
} catch (runtime_error err) {
cout<<err.what()<<"\nTry again? Enter y or n"<<endl;
char c;
cin>>c;
if( c == 'n' )
break;
}
}
return 0;
}
try块
try块的通用语法形式是:
try {
program-statements
} catch ( exception-specifier) {
handler-statements
} catch ( exception-specifier) {
handler-statements
} //...
try块以关键字try开始,后面是用花括号括起来的语句序列块.try块后面是一个或多个catch子句. 每个catch子句包括三部分 : 关键字catch,圆括号内单个类型或者单个对象的声明------称为异常说明符,以及通常用花括号括起来的语句块.如果选择了一个catch子句来处理异常,则执行相关的块语句.一旦catch子句执行结束,程序流程立即执行紧随着最后一个catch子句的语句.
try语句内program-statement形成程序的正常逻辑.这里面可以包含任意c++语句,包括变量声明.与其他块语句一样,try块引入局部作用域,在try块中声明的变量,包括catch子句中声明的变量,不能在try外面引用.
函数在寻找处理代码的过程中退出
抛出一个异常时,首先搜索的是抛出异常的函数.如果没有找到匹配的catch,则终止这个函数的执行,并在调用这个函数的函数中寻找相配的catch.如果仍然没有找到匹配的catch,则终止这个函数的执行,搜索调用它的函数.如此类推,继续按执行路径回退,直到找到适当类型的catch为止.
如果不存在处理该异常的catch子句,程序的运行就要跳转到名为terminate的标准库函数,该函数在excepttion头文件中定义.这个标准库函数的行为依赖于系统,通常情况下,他的执行将导致程序非正常退出.
标准异常
c++标准库定义了一组类,用于报告在标准库中函数遇到的问题. 标准库异常定义在四个头文件中:
(1) exception头文件定义了最常见的异常类,它的类名是exception.这个类只通知异常的产生,但不会提供更多的信息.
(2)stdexcept头文件定义了几种常见的异常类
(3)new头文件定义了bad_alloc异常类型,提供因无法分配内存而由new抛出的异常.
(4)type_info头文件定义了bad_cast异常类型.
在<stdexcept>头文件中定义的标准异常类
exception 最常见的问题
runtime_error 运行时错误: 仅在运行时才能检测到的问题
range_error 运行时错误: 生成的结果超出了有意义的值域范围
overflow_error 运行时错误: 计算上溢
underflow_error 运行时错误: 计算下溢
logic_error 逻辑错误: 可以在运行前检测到的问题
domain_error 逻辑错误: 参数的结果值不存在
invalid_argument 逻辑错误: 不合适的参数
length_error 逻辑错误: 试图生成一个超出该类型最大长度的对象
out_of_range 逻辑错误: 使用一个超出有效范围的值
标准库异常类
标准库异常类只提供很少的操作, 包括创建,复制异常类型对象以及异常类型对象的赋值. exception, bad_alloc以及bad_cast类型只定义了默认构造函数, 无法在创建这些类型对象时为它们提供初值. 其他的异常类型则只定义了一个使用string初始化式的构造函数,当需要定义这些异常类型的对象时,必须提供一个string参数. string初始化式用于为所发生的错误提供更多的信息.
异常类型只定义了一个名为what的操作.这个函数不需要任何参数,并且返回const char* 类型的值. 它返回的指针指向一个C风格字符串.使用c风格字符串的目的是为所抛出的异常提供更详细的文字描述.
assert预处理宏
assert宏是在cassert头文件中定义的,所有使用assert的文件都必须包含这个头文件.
预处理宏有点像函数调用. assert宏需要一个表达式作为它的条件:
assert(expr)
如果expr的结果为false,assert输出信息并且终止程序的执行.如果该表达式有一个非0值,则assert不做任何操作.一般使用assert来测试"不可能发生"的条件.