1
异常使用示例:
try {
for ( int ix = 1; ix < 51; ++ix )
{
if ( ix % 3 == 0 )
stack.push( ix );
if ( ix % 4 == 0 )
stack.display();
if ( ix % 10 == 0 ) {
int dummy;
stack.pop( dummy );
stack.display();
}
}
}
catch ( pushOnFull ) { ... }
catch ( popOnEmpty ) { ... }
其中的push, pop 在栈满和空时这样抛出异常:
// 表达式是一个构造函数调用
throw popOnEmpty();
当然,也可以抛出其他类型的对象。
在我们的例子中 程序的控制流是下列几种情况之一
1 如果没有异常发生 则执行try 块中的代码 和try 块相关联的处理代码被忽略 程
序main()返回0
2 如果在for 循环的第一个if 语句中调用的成员函数push()抛出一个异常 则for 循环
的第二个和第三个if 语句被忽略 该for 循环和try 块被退出 执行pushOnFull 类型异常的
处理代码
3 如果在for 循环的第三个if 语句中调用的成员函数pop()抛出一个异常 则针对display()
的调用被忽略 for 循环和try 块被退出 执行popOnEmpty 类型异常的处理代码
当某条语句抛出异常时 跟在该语句后面的语句将被跳过 程序执行权被转交给处理异
常的catch 子句 如果没有catch 子句能够处理该异常 则程序执行权又将被转交给C++标准
库中定义的函数terminate() 。
2
C++的异常处理机制被称为是不可恢复的 nonresumptive 一旦异常被处理 程序的
执行就不能够在异常被抛出的地方继续 在我们的例子中 一旦异常被处理 程序的执行就
不能够在pop()成员函数中异常被抛出的地方继续
3
异常对象总是在抛出点被创建 即使throw 表达式不是一个构造函数调用 或者它没有
表现出要创建一个异常对象 情况也是如此 例如
enum EHstate { noErr, zeroOp, negativeOp, severeError };
enum EHstate state = noErr;
int mathFunc( int i ) {
if ( i == 0 ) {
state = zeroOp;
throw state; // 创建异常对象
}
// 否则, 正常处理流程继续
}
在这个例子中 对象state 没有被用作异常对象 而是由throw 表达式创建了一个类型为
EHstate 的异常对象 并且用全局对象state 的值初始化该对象
后面catch可以这样用:
void calculate( int op ) {
try {
mathFunc( op );
}
catch ( EHstate &eObj ) {
// eObj 是被抛出的异常对象的引用
}
}
4
throw 表达式的行为有点像函数调用 而catch 子句有点像函数定义 这两种机制的一个主要区别是
建立函数调用所需要的全部信息在编译时刻已经获得 而对异常处理机制则不然 C++异常
处理要求运行时刻的支持
5
如果有一个异常被抛出 则资源res 的释放被跳过去 为保证该资源被释放 我们不是
为每种可能的异常都写一个catch 子句 因为我们不知道可能被抛出的全部异常 但是我们
可以使用catch 子句catch-all 这种catch 子句有一个形式为 ... 的异常声明 这里的二个点
被称为省略号 ellipsis 对任何类型的异常 都会进入这个catch 子句 例如
// 对任何异常都会进人
catch ( ... ) {
// 这里是我们的代码
}
6
在异常处理过程中也可能存在 单个catch 子句不能完全处理异常 的情况 在某些修正动作之后 catch 子句
可能决定该异常必须由函数调用链中更上级的函数来处理 那么catch子句可以通过重新抛出 rethrow
该异常 把异常传递给函数调用链中更上级的另一个catch 子句
rethrow 表达式的形式为
throw;
rethrow 表达式重新抛出该异常对象 rethrow 只能出现在catch 子句的复合语句中 例
如
catch ( exception eObj ) {
if ( canHandle( eObj ) )
// 处理异常
return;
else
// 重新抛出它, 并由另一个 catch子句来处理
throw;
}
被重新抛出的异常就是原来的异常对象
7 异常规范 exception specification 提供
了一种方案 它能够随着函数声明列出该函数可能抛出的异常 它保证该函数不会抛出任何
其他类型的异常
异常规范跟随在函数参数表之后 它用关键字throw 来指定 后面是用括号括起来的异
常类型表 例如 我们可以如下修改iStack 类的成员函数的声明 以增加适当的异常规范
class iStack {
public:
// ...
void pop( int &value ) throw(popOnEmpty);
void push( int value ) throw(pushOnFull);
private:
// ...
};
对于pop()的调用 保证不会抛出任何popOnEmpty 类型之外的异常