12-了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异
第三章,异常
条款12 了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异
描述
(1)异常捕获catch子句与函数声明语法看起来一样,有by reference 和by value方式传递参数,但是catch子句与函数调用不同,调用函数后控制权会回到调用端,但是当抛出异常后,控制权不会回到抛出端。
(2)catch子句捕获总是会将传递的对象做拷贝,因为抛出异常后,便离开了原生对象的生存空间。即使,对象为static修饰的静态对象(存在直到程序结束),在抛出异常时捕获仍然会拷贝产生一个副本; 以by reference捕捉,仍然不能修改原对象,只能修改其拷贝后的副本。
(3)当对象被复制作为一个exception时,调用的是copy constructot;这个copy constructor相应于该对象的“静态类型”(基类),而非“动态类型”
class Widget{...}; |
小结: exception对象是其他对象的副本
(4)差别:
catch(Widget& w) |
(5)捕捉方式
catch (Widget w) ... //by value |
by value捕捉会产生两个副本,一个是任何exception都会产生的临时对象,另一个将临时对象复制到w上;而 by refernce只有第一种复制行为
(6)千万不要抛出指向局部对象的指针,因为离开生存空间后得到的是指向被销毁对象的指针。
(7)”exception传播” 不同于函数调用”参数传递”的类型吻合规则
double sqrt(double); // from <cmatch> or <match.h> |
try语句块抛出的int exception绝不会被“捕捉double exception”的catch子句捕获!这期间不会有类型转换的行为发生,int exception只能被catch(int i)捕获。
(8)exception与catch子句相匹配的过程中,只有两种转换可以发生;
一是“继承架构中的类转换” ,一个针对base class exception编写的catch子句可以处理类型为deried class的exception,这和前面提到的“静态类型”识别是一个思路。一个可接收最根源类的catch子句可以捕捉此继承体系下的所有exception。
这种所谓的“继承架构中的exception转换”规则可适用于by value,by reference以及by pointer 这3种形式
catch (runtime_error) ... //by value |
第二个允许发生的转换是从“有型指针”转为“无型指针”,所以一个catch(const void*)可以捕捉任何指针类型的exception
(9)”exception传播” 不同于函数调用”参数传递”的依出现顺序做匹配尝试
try{ |
虚函数采用的是”best fit”(最佳吻合),而exception采用的是”first fit”(最先吻合)所以上述代码会收到编译器的警告,或者有的编译器会报错!因为第二个catch子句不会接收到任何异常,没有任何意义。
如果是必须的需求设计,就应该将catch (derivedClass& dc)写在catch (baseClass& bc)之前!
总结:
“传递对象到函数中,或是以对象调用虚函数”和“将对象抛出成为一个exception传播”之间,有3个主要差异:
1、exception objects总是会被复制生成一个临时对象,by value接收,会生成两个对象;而传递给函数参数的对象不一定会被复制
2、被抛出成为exception的对象,其被允许转换类型的动作比传递到函数去的对象少
3、catch子句第一个匹配成功的就执行,而虚函数调用是执行“与对象类型最佳吻合”的函数。
