从语法上看,在函数里声明参数与在catch子句中声明参数是一样的,catch里的参数可以是值类型,引用类型,指针类型。例如:
try { ..... } catch(A a) { } catch(B& b) { } catch(C* c) { } |
尽管表面是它们是一样的,但是编译器对二者的处理却又很大的不同。调用函数时,程序的控制权最终还会返回到函数的调用处,但是抛出一个异常时,控制权永远不会回到抛出异常的地方。
class A; void func_throw() { A a; throw a; //抛出的是a的拷贝,拷贝到一个临时对象里 } try { func_throw(); } catch(A a) //临时对象的拷贝 { } |
当我们抛出一个异常对象时,抛出的是这个异常对象的拷贝。当异常对象被拷贝时,拷贝操作是由对象的拷贝构造函数完成的。该拷贝构造函数是对象的静态类型(static type)所对应类的拷贝构造函数,而不是对象的动态类型(dynamic type)对应类的拷贝构造函数。此时对象会丢失RTTI信息。
异常是其它对象的拷贝,这个事实影响到你如何在catch块中再抛出一个异常。比如下面这两个catch块,乍一看好像一样:
catch (A& w) // 捕获异常 { // 处理异常 throw; // 重新抛出异常,让它继续传递 } catch (A& w) // 捕获Widget异常 { // 处理异常 throw w; // 传递被捕获异常的拷贝 } |
第一个块中重新抛出的是当前异常(current exception),无论它是什么类型。(有可能是A的派生类)
第二个catch块重新抛出的是新异常,失去了原来的类型信息。
一般来说,你应该用throw来重新抛出当前的异常,因为这样不会改变被传递出去的异常类型,而且更有效率,因为不用生成一个新拷贝。
看看以下这三种声明:
catch (A w) ... // 通过传值 catch (A& w) ... // 通过传递引用 catch (const A& w) ... //const引用 |