catch子句
当try块中的语句抛出异常时,系统通过查看跟在其后的catch子句列表,来查找可处理该异常的catch子句。catch子句由三部分组成:关键字catch、圆括号中的异常声明以及复合语句中的一组语句。
catch子句不是函数,所以圆括号中不是形参,而是一个异常类型声明,可以是类型也可以是对象。
catch子句只有一个子句,没有定义和调用之分。使用时由系统按规则自动在catch子句列表中匹配。
catch子句可以包含返回语句(return),也可不包含返回语句。包含返回语句,则整个程序结束。而不包含返回语句,则执行catch列表之后的下一条语句。
catch子句异常声明可以是一个对象声明。以栈为例。当栈满时,要求在异常对象中保存不能被压入到栈中的值,pushOnFull类可定义如下:
template <typename T>class pushOnFull
{
T _value;
public:
pushOnFull(T i):_value(i){}
//或写为pushOnFull(T i){_value=i;}
T value()
{return _value;}
};
新的私有数据成员_value保存那些不能被压入栈中的值。该值即调用构造函数时的实参。对应在throw表达式中,构造抛出对象也要有实参:
throw pushOnFull(data);
//data即Push(const &data)中的参数data
在catch子句中,要取得_value,须调用pushOnFull中的成员函数value():
catch(pushOnFull<T> eObj)
{
cerr<<”栈满”<<eObj.value()<<”未压入栈”<<endl;
return 1;
}
在catch子句中声明了对象eObj,用它来调用pushOnFull类的对象成员函数value()。
异常对象是在抛出点被创建,与catch子句是否显式要求创建一个异常对象无关,该对象总是存在,在catch子句中只是为了 调用异常处理对象的成员函数 才声明对象eObj,不用类。
*catch子句异常声明中采用对象只是一种形式。甚至异常并非一个类对象时,也可以用同样的格式,比如异常为一枚举量,这时就等效于按值传递。
catch子句的异常声明与函数参数声明类似,可以是按值传送,也可以是按引用传递。对大型类对象减少不必要的复制是很有意义的,所以对于类类型的异常,其异常声明最好也是被声明为引用。如:
catch(pushOnFull<T> & eObj)
{
cerr<<”栈满”<<eObj.value()<<”未压栈”<<endl;
return 1;
}
使用引用类型的异常声明,catch子句能够修改异常对象,但仅仅是异常对象本身,正常程序部分的量并不会被修改。
与一般类对象不同,实际上异常对象处理完后,生命期也就结束了。只有需要重新抛出异常(在下一节中讨论),修改操作才有意义。
【例10.1】包含栈满或空异常的完整的程序。( 查看源码 )
函数try块的使用
把程序的正常处理代码和异常处理代码分离的最清楚的方法是定义函数try块(Function try Block)。这种方法是把整个函数包括在try块中。一个函数try块把一组catch子句同一个函数体相关联。如果函数体中的语句抛出一个异常,则考虑跟在函数体后面的处理代码来处理该异常。函数try块对构造函数尤其有用。
【例10.1_1】定义函数try块(Function try Block)。( 查看源码 )