“抛出一个Exception”与“传递一个参数”的区别
两个类比的概念,下面的“==”表示类比的意思:
调用端==抛出端
被调端==捕获端
1。调用函数,控制权最终会回到调用端;但抛出异常后,控制权就不会再回到抛出端了;
2。一个对象抛出时总是要发生拷贝(即扔出一个副本),而一个参数被传递时却不会发生拷贝(即扔出对象本身)。注意,这里只谈道抛出和传递,而未涉及到接受和捕获。
throw obj;//总会调用obj的copy constructor,如果obj为对象。
****:这里必须要提及的一点是“拷贝动作永远都是以对象的静态型别为本的”,这是C++的一条通用的适用原则。
3。throw;//这个句子有点特殊,它是将捕获到的异常抛出去,因此它必须位于catch块内才可以使用。此外,这种抛出并不会复制对象(因为它没有抛出对象诸如throw obj;而是抛出异常,因此捕获到啥就抛出啥),因此虽然第二条法则是真理,但是大家也千万不要妄自揣测——catch到的都是副本,因为这里就告诉了你有可能不是的,因为throw;这种特殊情况的存在。
4。catch(A a);和catch(A& a);还有catch(const A& a);三种方式都可以捕捉到类型为A的异常对象,第一种方式和第二种方式的区别在于接受异常对象时是否需要付出再拷贝一次的代价,即第一种方式需要把扔出来的对象再拷贝一次给对象a;第二种和第三种方式基本差不多,都是引用类型,只不过第三种方式中不能够修改a的内容罢了。现在知道了吧,throw a;catch(A a);这两句话会造成调用两次A的拷贝构造函数。
此外,大家应该知道C++在函数调用过程中的一个准则是:传递临时对象时,接受对象的形参不能是引用类型,除非是const引用类型。而在catch端就没有这种限制了,对于抛出来的临时对象,catch在接收它们时能够以引用的方式来接收。
5。从实参到形参,中间可以进行的对象类型转换有很多种,编译器可以隐士的为你做这些事情,但是从抛出的对象到catch端捕获的对象中间经历的对象转换却只有两种了:即在继承体系中的向上塑型和具体型别的指针到void*指针的转换。
6。其实也可以捕获指针类别的异常,与捕获其他对象没有区别,但是如果抛出指针对象,那么虽然也会进行拷贝,但此时拷贝的仅仅是指针而已,而不是指针所指向的对象。但是需要注意的是,这需要注意的是,不要让该指针指向一个被定义在try块内的临时变量,因为一旦转移到catch块时,try块内的对象都会被销毁,此时catch块内的指针实则指向一个不存在的对象。下面这段代码会为你演绎抛出指针的情况:
A a;
try{
a.m_nA=999;
throw &a;
}catch(A* pa){
cout<<pa->m_nA<<endl;
pa->m_nA=888;
try{
throw pa;
}
catch(A* pa){
cout<<pa->m_nA<<endl;
}
}