8. 怎么捕捉全部异常或未知异常?
可以用catch ( … ) { } .
作用在于:1. 可以释放在前面获得的资源(如动态内存),因为异常退出,这些资源为释放。2. 捕获其余类型的未知异常。
catch 子句被检查的顺序与它们在try 块之后出现的顺序相同。一旦找到了一个匹配,则后续的catch 子句将不再检查。这意味着如果catch(…)与其他catch 子句联合使用,它必须总是被放在异常处理代码表的最后,否则就会产生一个编译时刻错误。例子如下:
catch ( pushOnFull ) {}
catch ( popOnEmpty ) { }
catch (…) { } // 必须是最后一个catch 子句
9. 为什么 catch 子句的异常声明通常被声明为引用?
1)可以避免由异常对象到 catch 子句中的对象的拷贝,特别是对象比较大时。
2)能确保catch子句对异常对象的修改能再次抛出。
3)确保能正确地调用与异常类型相关联的虚拟函数,避免对象切割。
具体参见4,7,17.
10. 异常对象的生命周期?
产生:throw className()时产生。
销毁:该异常的最后一个catch 子句退出时销毁
注意:因为异常可能在catch子句中被重新抛出,所以在到达最后一个处理该异常的catch 子句之前,异常对象是不能被销毁的。
11. const char *到char * 非法的异常类型转换。
我们注意到下面的代码在VC中可以正常运行(gcc不能)。
try { throw "exception";}
catch (char *) {cout 《 "exception catch!" 《endl;}
实际上throw的是一个const char *, catch的时候转型成char *.这是C++(www.cppentry.com)对C的向下兼容。
同样的问题存在于:
1. char *p = "test"; // 也是一个const char * 到char *转型。
2. void func(char* p) { printf("%s\n", p); }
func("abc"); // const char * 到char *
以上两例在编译时不警告,运行时不出错,是存在隐患的。
12. 异常规范(exception specification)的概念?
异常规范在函数声明是规定了函数可以抛出且只能抛出哪些异常。空的异常规范保证函数不会抛出任何异常。如果一个函数声明没有指定异常规范,则该函数可以抛出任何类型的异常。
例1:函数Pop若有异常,只能抛出popOnEmpty和string类型的异常对象
void pop( int &value ) throw(popOnEmpty, string);
例2:函数no_problem()保证不会抛出任何异常
extern void no_problem() throw();
例3:函数problem()可以抛出任何类型的异常
extern void problem();
13. 函数指针的异常规范?
我们也可以在函数指针的声明处给出一个异常规范。例如:
void (*pf) (int) throw(string);
当带有异常规范的函数指针被初始化或被赋值时,用作初始值或右值的指针异常规范必须与被初始化或赋值的指针异常规范一样或更严格。例如:
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;
注:在VC和gcc上测试失败。
14. 派生类中虚函数的异常规范的声明?
基类中虚拟函数的异常规范,可以与派生类改写的成员函数的异常规范不同。但是派生类虚拟函数的异常规范必须与基类虚拟函数的异常规范一样或者更严格。
class Base {
public:
virtual double f1( double ) throw ();
virtual int f2( int ) throw ( int );
virtual string f3( ) throw ( int, string );
// …
};
class Derived : public Base {
public:
// error: 异常规范没有 base::f1() 的严格
double f1( double ) throw ( string );
// ok: 与 base::f2() 相同的异常规范
int f2( int ) throw ( int );
// ok: 派生 f3() 更严格
string f3( ) throw ( int );
// …
};
15. 被抛出的异常的类型和异常规范中指定的类型能进行类型转换吗?
int convert( int parm ) throw(string)
{
if ( somethingRather )
// 程序错误:
// convert() 不允许 const char* 型的异常
throw "help!";
}
throw 表达式抛出一个C 风格的字符串,由这个throw 表达式创建的异常对象的类型为const char*.通常,const char*型的表达式可以被转换成string 类型。但是,异常规范不允许从被抛出的异常类型到异常规范指定的类型之问的转换。
注意:
当异常规范指定一个类类型(类类型的指针)时,如果一个异常规范指定了一个类,则该函数可以抛出"从该类公有派生的类类型"的异常对象。类指针同理。
例如:
class popOnEmpty : public stackExcp { };
void stackManip() throw( stackExcp ) // 异常规范是stackExcp类型
{
throw stackExcp(); // 与异常规范一样
throw popOnEmpty (); // ok. 是stackExcp的派生类
}
16. 公有基类的catch子句可以捕捉到其派生类的异常对象。
int main( ) {
try {
// 抛出pushOnFull异常
}
catch ( Excp ) {
// 处理 popOnEmpty 和 pushOnFull 异常
throw;
}
catch ( pushOnFull ) {
// 处理 pushOnFull 异常
}
}
在上例中,进入catch ( Excp )子句,重新抛出的异常任然是pushOnFull类型的异常对象,而不会是其基类对象Excp.