“拋出一個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;
}
}