用于大型程序的工具
--异常处理[续1]
四、又一次抛出
有可能单个catch不能全然处理一个异常。在进行了一些校正行动之后,catch可能确定该异常必须由函数调用链中更上层的函数来处理,catch能够又一次抛出将异常传递给函数调用链中更上层的函数。又一次抛出是后面不跟类型或表达式的一个throw:
throw;
空throw语句将又一次抛出异常对象,它仅仅能出如今catch或从catch调用的函数中。假设在处理代码不活动时碰到空throw,就调用terminate函数。
尽管又一次抛出不指定自己的异常,但仍然将一个异常对象沿链向上传递,被抛出的异常是原来的异常对象,而不是catch形參。当catch形參是基类类型的时候,我们不知道由又一次抛出表达式抛出的实际类型,该类型取决于异常对象的动态类型,而不是catch形參的静态类型。比如,来自带基类类型形參catch的又一次抛出,可能实际抛出一个派生类型的对象。
一般而言,catch能够改变它的形參。在改变它的形參之后,假设catch又一次抛出异常,那么仅仅有当异常说明符是引用的时候,才会传播那些改变。
catch (my_error &eObj) { eObj.status = severeErr; throw; //传播改变 } catch (other_error eObj) { e.status = badErr; throw; //仅仅是复制对象 }
五、捕获全部异常的处理代码
能够使用捕获全部异常catch子句来处理不知道确切类型的异常。捕获全部异常的catch子句形式为(...),比如:
catch (...) { //... }
捕获全部异常的catch子句与随意类型的异常都匹配。
catch(...)常常与又一次抛出表达式结合使用,catch完毕可做的全部局部工作,然后又一次抛出异常:
void manip() { try { //... } catch (...) { //... throw; } }
【注解】
假设catch(...)与其它catch子句结合使用,它必须是最后一个,否则,不论什么跟在它后面的catch子句都将不能被匹配。
六、函数測试块与构造函数
在进入构造函数函数体之前处理构造函数初始化式,构造函数函数体内部的catch子句不能处理在处理构造函数初始化式时可能发生的异常。
为了处理来自构造函数初始化式的异常,必须将构造函数编写为函数try块。能够使用函数測试块将一组catch子句与函数联成一个总体。
template <class T> Handle<T>::Handle(T *p) try : ptr(p),use(new size_t(1)) { //empty function body } catch (const std::bad_alloc &e) { handle_out_of_memory(e); }
注意:关键词try出如今成员初始化列表之前,而且測试块的复合语句包围了构造函数的函数体。catch子句既能够处理从成员初始化列表中抛出的异常,也能够处理从构造函数函数体中抛出的异常!
因此:构造函数要处理来自构造函数初始化式的异常,唯一方法是将构造函数编写为函数測试块!
//P587 习题17.5 //(a) class exceptionType {}; throw new exceptionType(); catch(exceptionType *pet) { //... }
//(b) throw 8; catch (...) { //... }
//(c) enum mathErr {overflow,underflow,zeroDivide}; throw overflow; catch (mathErr &ref) { //... }
//(d) typedef int EXCEPTYPE; throw 250; catch(EXCEPTYPE) { //... }
七、异常类层次
exception类型所定义的唯一操作是一个名为what的虚成员,该函数返回const char *对象,它一般返回用来在抛出位置构造异常对象的信息。由于what是虚函数,假设捕获了基类类型的引用,对what函数的调用将执行适合异常对象的动态类型的版本号。
1、用于书店应用程序的异常类
应用程序常常通过从exception类或者中间基类派生附加类型来扩充exception层次。这些新派生的类能够表示特定于应用程序领域的异常类型。
标准exception类层次
我们能够定义自己的异常层次来表示可能出现的特定于应用程序的问题。
class out_of_stock : public std::runtime_error { public: explicit out_of_stock(const std::string &s): std::runtime_error(s) {} }; class isbn_mismatch : public std::logic_error { public: isbn_mismatch(const std::string &s):std::logic_error(s) {} isbn_mismatch(const std::string &s, const std::string &lhs,const std::string &rhs): std::logic_error(s), left(lhs),right(rhs) {} const std::string left,right; virtual ~isbn_mismatch() throw() {} };
通过从标准异常类派生,定义了特定于应用程序的异常类型,能够觉得异常类按层次组织:随着层次的加深,每一层变得更特殊的异常。
第一层即最一般的层由exception类代表,当捕获这一类型的对象时,我们所知道的仅仅是有些地方出错了。
第二层将exception特化为两个大类:执行时错误和逻辑错误。我们的书店异常类表示更特化的层中的事件。out_of_stock类表示可能在执行时出现故障的特定于应用程序的事情,能够用它发出不能履行某个订单的信号。isbn_mismatch异常是从logic_error派生的更特殊的异常,原则上,程序能够通过调用same_isbn检測到不匹配的ISBN。
2、使用程序猿定义的异常类型
用和使用标准库类同样的方法使用自己的异常类。
Sales_item operator+(const Sales_item &lhs,const Sales_item &rhs) { if (!lhs.same_isbn(rhs)) { throw isbn_mismatch("isbn mismatch",lhs.book(),rhs.book()); } Sales_item ret(lhs); ret += rhs; return ret; }
然后,使用加操作符的代码能够检測这个错误,写出适当的错误消息:
Sales_item item1,item2,sum; while (cin >> item1 >> item2) { try { sum = item1 + item2; } catch (const isbn_mismatch &e) { cerr << e.what() << ": left isbn(" << e.left << "),right isbn(" << e.right << ")" << endl; } }