异常规范(exception specification)提供了一种方案:它能够随着函数声明列出该函数可能抛出的异常,它保证该函数不会抛出任何其他类型的异常,异常规范跟随在函数参数表之后,它用关键字throw来指定,后面是用括号括起来的异常类型表。
例如:我们可以如下修改iStack类的成员函数的声明,以增加适当的异常规范:
class iStack
{
public:
// ...
void pop(int & value) throw(popOnEmpty);
void push(int value) throw(pushOnFull);
private:
// ...
};
如果函数声明指定了一个异常规范,则同一函数的重复声明必须指定同一类型的异常规范,同一函数的不同声明上的异常规范是不能累积的。
例如:
// 同一函数的两个声明
extern int foo(int param = 0) throw(string);
// 错误: 异常规范被省略
extern int foo(int param) { }
如果函数抛出了一个没有被列在异常规范中的异常会怎么样?程序只有在遇到某种不正常情况时,异常才会被抛出,在编译时刻编译器不可能知道在执行时。程序是否会遇到这些异常,因此一个函数的异常规范的违例,只能在运行时刻才能被检测出来,如果函数抛出了一个没有被列在其异常规范中的异常,则系统调用C++标准库中定义的函数unexpected(),unexpected()的缺省行为是调用terminate(),在某些条件下可能有必要改变unexpected()执行的动作,C++标准库提供了一种机制可让我们改变unexpected()的缺省行为。
1 空的异常规范保证函数不会抛出任何异常,
例如:函数no_problem()保证不会抛出任何异常
extern void no_problem() throw();
2 如果一个函数声明没有指定异常规范则该函数可以抛出任何类型的异常,在被抛出的异常类型与异常规范中指定的类型之间不允许类型转换。
例如:
int convert(int parm) throw(string)
{
// ...
if (somethingRather)
{
// 程序错误:
// convert() 不允许 const char* 型的异常
throw "help!";
}
}
在函数convert()中的throw表达式抛出一个C风格的字符串,由这个throw表达式创建的异常对象的类型为const char*,通常const char*型的表达式可以被转换成string类型,但是异常规范不允许从被抛出的异常类型到异常规范指定的类型之问的转换,如果convert()抛出该异常,则调用函数unexpected(),为了修正这种情况,可以如下修改throw表达式,显式地把表达式的值转换成string类型:
throw string("help!");
3 我们也可以在函数指针的声明处给出一个异常规范,
例如:
void (*pf) (int) throw(string);
该声明表示pf是一个函数指针,它只能抛出string类型的异常和函数声明一样,同一指针的不同异常规范不能累积,指针pf的所有声明都必须指定相同的规范,
例如:
// 函数指针的声明
extern void (*pf)(int) throw(string);
// 同一个函数指针的定义,错误: 缺少异常规范
void (*pf) (int);
void recoup(int, int) throw(exceptionType);
void no_problem() throw();
void doit(int, int) throw(string, exceptionType);
// ok: recoup()与pf1的异常规范一样严格
void (*pf1)(int, int) throw(exceptionType) = &recoup;
// ok: no_problem()比pf2更严格
void (*pf2)() throw(string) = &no_problem;
// 错误: doit()没有pf3严格
void (*pf3)(int, int) throw(string) = &doit;