版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
1、第十五章 異常 Chapter 15 Exception 第1頁/共63頁異常是一種程序控制機制,與函數(shù)機制獨立和互補 函數(shù)是一種以棧結構展開的上下函數(shù)銜接的程序控制系統(tǒng),異常是另一種控制結構,它依附于棧結構,卻可以同時設置多個異常類型作為網(wǎng)捕條件,從而以類型匹配在棧機制中跳躍回饋.異常設計目的: 棧機制是一種高度節(jié)律性控制機制,面向?qū)ο缶幊虆s要求對象之間有方向、有目的的控制傳動,從一開始,異常就是沖著改變程序控制結構,以適應面向?qū)ο蟪绦蚋行У毓ぷ鬟@個主題,而不是僅為了進行錯誤處理。異常設計出來之后,卻發(fā)現(xiàn)在錯誤處理方面獲得了最大的好處第2頁/共63頁第十五章內(nèi)容第十五章內(nèi)容1.錯誤處理的復
2、雜性(Error Processing Complexity) 2. 使用異常(Using Exception) 3.捕捉異常(Catching Exception) 4. 異常申述(Exception Description) 5. 異常繼承體系(Exception Inheritance System) 6. 異常應用 (Exception Applications) 7. 非錯誤處理(Non-Error Processing)第3頁/共63頁15.1.1 錯誤種類 C+語言是按函數(shù)調(diào)用機制展開程序執(zhí)行的,一般對處理錯誤的編程有下列常規(guī)手段: 1.遇到錯誤,立即終止程序運行。如打不開文件,
3、或者讀不到所要求的數(shù)據(jù),則只能終止運行:第4頁/共63頁 #include #include #include using namespace std; void fn() ifstream in(abc.txt); if(!in) couta; coutsqrt(a*1.0)n) if(a0) cout=size)下標溢出接受外來申請if(vi溢出) 說明資源不足應用模塊2應用模塊1向量模塊第10頁/共63頁15.1.3 調(diào)用鏈的牽制 函數(shù)是棧式管理的,從一個被調(diào)用的函數(shù)現(xiàn)場要把信息傳遞到相隔若干調(diào)用的主調(diào)函數(shù),需要逐層退棧,一邊退棧,一邊通過函數(shù)返回值回饋,而且,每個函數(shù)都需要相應的接受判
4、斷以及接力傳遞的工作。如有一個簡單的系列文件處理程序,假定文件在打開前,要經(jīng)歷文件名確定和打開方式確定的工作,如圖15-2:第11頁/共63頁圖15-2 鏈式調(diào)用與出錯后跳躍返回主函數(shù)文件名處理打開方式處理打開輸入文件打開輸出文件出錯跳回第12頁/共63頁 #include #include using namespace std; void procFileName(string s); bool procOpenMode(string s); bool openIn(string s); bool openOut(string s); int main() procFileName(iab
5、c); procFileName(oabc); void procFileName(string s) for(char c=0; c=9; c+) string t = s + c + .txt; if(!procOpenMode(t) couterror opening tn; return; 第13頁/共63頁 bool procOpenMode(string s) if(s0=i & openIn(s) | s0=o & openOut(s) return true; return false; bool openIn(string s) ifstream in(s.c
6、_str(); if(!in) return false; for(string line; getline(in, line); coutlinen); return true; bool openOut(string s) fstream out(s.c_str(), ios:in|ios:out|ios:ate); if(!out) return false; couts+ is here.n; outs+ is ok.n; return true;第14頁/共63頁15.2 使用異常 異常是專門針對抽象編程中的一系列錯誤處理的,C+中不能借助函數(shù)機制,因為棧結構的本質(zhì)是先進后出,依次訪
7、問,無法進行跳躍,但錯誤處理的特征卻是遇到錯誤信息就想要轉(zhuǎn)到若干級之上進行重新嘗試,如圖:第15頁/共63頁v錯誤處理示意:放棄一棵子樹,循調(diào)用鏈跳到祖先函數(shù)發(fā)現(xiàn)錯誤處第16頁/共63頁 異常超脫于函數(shù)機制,決定了其對函數(shù)的跨越式回跳。第17頁/共63頁15.2.1 異常使用三部曲 1.框定異常(try 語句塊) 在祖先函數(shù)處,框定可能產(chǎn)生錯誤的語句序列,它是異常的根據(jù),若不框定異常,則沒有異常。 2.定義異常處理(catch 語句塊) 將出現(xiàn)異常后的處理過程放在catch塊中,以便當異常被拋出,因類型匹配而捕捉時,就處理之。 3.拋擲異常(throw 語句) 在可能產(chǎn)生異常的語句中進行錯誤檢
8、測,有錯就拋擲異常 第18頁/共63頁19異常處理的實現(xiàn)機制 拋擲異常的程序段void Fun().throw 表達式;. 捕獲并處理異常的程序段try 復合語句catch(異常類型聲明) 復合語句catch(類型 (形參) 復合語句 第19頁/共63頁20例12-1處理除零異常#include#includeint Div(int x,int y);int Div(int x,int y);int main()int main() trytry cout5/2=Div(5,2)endl; cout5/2=Div(5,2)endl; cout8/0=Div(8,0)endl; cout8/0=
9、Div(8,0)endl; cout7/1=Div(7,1)endl; cout7/1=Div(7,1)endl; catch(int) catch(int) coutexcept of deviding zero.n; coutexcept of deviding zero.n; coutthat is ok.n;coutthat is ok.n; int Div(int x,int y)int Div(int x,int y) if(y=0) throw y;if(y=0) throw y;return x/y;return x/y; 程序運行結果如下:5/2=25/2=2except o
10、f deviding zero.except of deviding zero.that is ok.that is ok.第20頁/共63頁 前兩個步驟是一個函數(shù)中定義的,而拋擲異常則可以跨函數(shù)使用。 當直接在try語句塊中使用時,異常處理退化為一般的錯誤處理模式。 在try語句塊中,會有一些語句調(diào)用了其他函數(shù),它們之間則構成一個調(diào)用鏈,在調(diào)用鏈中的某一個結點上,如果出現(xiàn)拋擲語句,則便是一般意義上的異常了。第21頁/共63頁22異常處理的基本思想函數(shù)f()捕獲并處理異常函數(shù)h() 引發(fā)異常函數(shù)g()調(diào)用者異常傳播方向調(diào)用關系第22頁/共63頁23異常處理的實現(xiàn)機制(續(xù)) 1 若有異常則通過t
11、hrow操作創(chuàng)建一個異常對象并拋擲。 2 將可能拋出異常的程序段嵌在try塊之中??刂仆ㄟ^正常的順序執(zhí)行到達try語句,然后執(zhí)行try塊內(nèi)的保護段。 3 如果在保護段執(zhí)行期間沒有引起異常,那么跟在try塊后的catch子句就不執(zhí)行。程序從try塊后跟隨的最后一個catch子句后面的語句繼續(xù)執(zhí)行下去。 4 catch子句按其在try塊后出現(xiàn)的順序被檢查。匹配的catch子句將捕獲并處理異常(或繼續(xù)拋擲異常)。 5 如果匹配的處理器未找到,則運行函數(shù)terminate將被自動調(diào)用,其缺省功能是調(diào)用abort終止程序。第23頁/共63頁24異常接口聲明 可以在函數(shù)的聲明中列出這個函數(shù)可能拋擲的所有異
12、常類型。 例如:void fun() throw(A,B,C,D); 若無異常接口聲明,則此函數(shù)可以拋擲任何類型的異常。 不拋擲任何類型異常的函數(shù)聲明如下:void fun() throw();第24頁/共63頁25異常處理中的構造與析構 找到一個匹配的catch異常處理后 初始化參數(shù)。 將從對應的try塊開始到異常被拋擲處之間構造(且尚未析構)的所有自動對象進行析構。 從最后一個catch處理之后開始恢復執(zhí)行。第25頁/共63頁26例12-2 異常處理時的析構#include void MyFunc( void );class Expt public: Expt(); Expt(); con
13、st char *ShowReason() const return Expt類異常。; ;void MyFunc() Demo D; cout在MyFunc()中拋擲Expt類異常。endl; throw Expt();class Demo public: Demo(); Demo();Demo:Demo() cout構造 Demo.endl;Demo:Demo() cout析構 Demo.endl;第26頁/共63頁int main() cout在main函數(shù)中。endl; try cout在try塊中,調(diào)用MyFunc()。 endl; MyFunc(); catch( Expt E )
14、 cout在catch異常處理程序中。endl; cout捕獲到Expt類型異常:; coutE.ShowReason()endl; catch( char *str ) cout捕獲到其他的異常:strendl; cout回到main函數(shù)。從這里恢復執(zhí)行。 endl; return 0;27第27頁/共63頁程序運行時輸出:在main函數(shù)中。在try塊中,調(diào)用MyFunc()。構造 Demo.在MyFunc()中拋擲Expt類異常。析構 Demo.在catch異常處理程序中。捕獲到Expt類型異常:Expt類異常。回到main函數(shù)。從這里恢復執(zhí)行。28第28頁/共63頁15.2.2 退化為普
15、通錯誤處理 如下面的程序是退化為普通錯誤處理的異常方式:第29頁/共63頁 #include #include using namespace std; int main(int argc, char* argv) ifstream in(argv1); try if(!in) throw string(argv1); catch(string s) couts+ File Opening Error.n; return 1; for(string s; getline(in, s); coutsendl ); 第30頁/共63頁 在這種簡單應用的場合,沒必要使用異常,可以用下列代碼替代: i
16、f(!in) cout“Error Opening File”argv1“n”; return 1; for(string s; getline(in, s); coutsendl );第31頁/共63頁15.2.3 跨越函數(shù)的異常處理 在中,是在出現(xiàn)錯誤時,從出現(xiàn)錯誤處到處理錯誤處,沿著函數(shù)調(diào)用的足跡,一步步往上返回,幾經(jīng)周折后,出錯信息早已丟失,剩下的只有出錯狀態(tài),程序改寫為異常處理方式處理,就可以實現(xiàn)跨函數(shù)的大跳轉(zhuǎn)了:第32頁/共63頁 #include #include using namespace std; void procFileName(string s); void pro
17、cOpenMode(string s); void openIn(string s); void openOut(string s); int main() procFileName(iabc); procFileName(oabc); void procFileName(string s) try for(char c=0; c=9; c+) procOpenMode(s + c+.txt); catch(string s) couterror opening s not existed.n;第33頁/共63頁 void procOpenMode(string s) if(s0=i) ope
18、nIn(s); else openOut(s); void openIn(string s) ifstream in(s.c_str(); if(!in) throw s+ inFile; for(string line; getline(in, line); coutlinen); void openOut(string s) fstream out(s.c_str(),ios:in|ios:out|ios:ate); if(!out) throw s+string( outFile); outs+ outFile is ok.n; couts+ is here.n;第34頁/共63頁15.
19、3.1 類型匹配 try語句中,涉及到類的,本質(zhì)上都是函數(shù)調(diào)用。 throw A() 異常機制與函數(shù)機制正交(互不干涉),但捕捉的方式是基于類型匹配。捕捉相當于函數(shù)返回類型的匹配,而不是函數(shù)參數(shù)的匹配,所以捕捉不用考慮一個拋擲中的多種數(shù)據(jù)類型匹配問題,如:第35頁/共63頁 #include using namespace std; class A; class B; int main() try int j=0; double d=2.3; char str20=Hello; couta; switch(a) case 1: throw d; case 2: throw j; case 3:
20、 throw str; case 4: throw A(); case 5: throw B(); default: coutNo throws here.n; 第36頁/共63頁 catch(int) coutint exception.n; catch(double) coutdouble exception.n; catch(char*) coutchar* exception.n; catch(A) coutclass A exception.n; catch(B) coutclass B exception.n; coutThats ok.n; /=第37頁/共63頁 catch代碼
21、塊必須出現(xiàn)在try后,并且在try塊后可以出現(xiàn)多個catch代碼塊,以捕捉各種不同類型的拋擲。 異常機制是基于這樣的原理:程序運行實質(zhì)上是數(shù)據(jù)實體在做一些操作,因此發(fā)生異?,F(xiàn)象的地方,一定是某個實體出了差錯,該實體所對應的數(shù)據(jù)類型便作為拋擲和捕捉的依據(jù)。第38頁/共63頁 拋擲的實體已經(jīng)存在,不需要通過類型轉(zhuǎn)換來創(chuàng)建一個臨時實體,實參與形參也不能用相容類型提升這一規(guī)則。另一方面,捕捉處理并不一定需要實參傳遞。第39頁/共63頁u 異常捕捉的類型匹配之苛刻程度可以和模板的類型匹配媲美,它不允許相容類型的隱式轉(zhuǎn)換,比如,拋擲char類型用int型就捕捉不到例如下列代碼不會輸出“int except
22、ion.”,從而也不會輸出“Thats ok.” 因為出現(xiàn)異常后提示退出int main()int main() try try throwthrow H; catchcatch(intint) coutint exception.n; coutThats ok.n;第40頁/共63頁15.3.2 撒網(wǎng)捕捉 程序中可以設置一道道捕捉的關卡,如果拋擲的異常,循著調(diào)用鏈往上,在最近的捕捉關卡未被捉住,則還會被更上端的捕捉關卡追捕,直逼到系統(tǒng)的最后一道防線, terminate.第41頁/共63頁15.4.1 申述異常 異常拋擲后總是沿著函數(shù)調(diào)用鏈往上,直到被某個函數(shù)捉住,因此,異常拋擲、捕捉以及處
23、理都依附于函數(shù),函數(shù)承載著異常。 上下游函數(shù)的聯(lián)系,可以通過異常申述來獲得,異常申述就是在函數(shù)聲明和函數(shù)定義的頭部加上可能拋擲的異常集合,如:第42頁/共63頁異常申述示例 void f() throw(A,B); void g(); void h() throw(); 說明f函數(shù)可能會拋擲出A和B類的異常,g函數(shù)內(nèi)可能會拋擲出任何類型的異常,h函數(shù)內(nèi)不會拋擲出任何異常。 如果h函數(shù)拋出異?;蛘遞函數(shù)拋擲出非A或非B類的異常,這是沒有預料到的,稱其為未料到異常。第43頁/共63頁 異常申述是一種對設計的描述,從而給程序員一個編程的參照,所以應作為界面,而放在函數(shù)聲明中,并通過頭文件的形式,擴散
24、到程序員那里。 其次,要使用異常申述,函數(shù)聲明和函數(shù)定義中的異常申述必須保持一致,否則無法一一對應。第44頁/共63頁沒有申述的函數(shù),默認為任何拋擲都可穿透該函數(shù)聲明:class A;class B;void f1()throw(A,B);void f2();void f3()throw();-對于函數(shù)g中的調(diào)用,可能捕捉到函數(shù)f1的和函數(shù)f2的異常拋擲 , 但 捕 捉 不 到 函 數(shù)f3中的任何拋擲VC中可以注:同一個函數(shù),其聲明與定義中的申述應一致class A;class B;void f1()throw(A,B) if(.) throw A();void f2() if(.) thro
25、w B();void f3()throw() if(.) throw A();void g() try f1(); f2(); f3(); catch(A) cout“exception in An”; catch(B) cout“exception in Bn”; 第45頁/共63頁15.4.2 捉不住處理 如果代碼中沒有徹底捕捉的異常處理,則對于拋擲的異常,有可能發(fā)生捕捉不住的情況。如向量的下標溢出時,系統(tǒng)會拋擲一個runtime_error異常,但許多程序都不屑一捕,因為程序都調(diào)通了,邏輯可靠,不會出現(xiàn)下標溢出的錯誤了。這類異常只能被系統(tǒng)默認的“強制捕捉器” terminate 捕捉了。
26、 Terminate是系統(tǒng)資源,默認操作是系統(tǒng)的abort函數(shù),從而無條件地終止程序的執(zhí)行。第46頁/共63頁 另外,未料到異常,如果不去理它,被系統(tǒng)截獲后會轉(zhuǎn)而執(zhí)行unexpected函數(shù),而unexpected函數(shù)的默認行為是執(zhí)行terminate函數(shù)。如:第47頁/共63頁捉不住處理:拋擲而無布網(wǎng)捕捉的異常將直逼系統(tǒng)的最后一道防線void f() if(.) throw A;void g() try f(); catch(B) cout“exception Bn”; int main() g();throw A將穿透函數(shù)f,g和main,抵達系統(tǒng)的最后一道防線激發(fā)terminate函數(shù)該
27、函數(shù)調(diào)用引起運行終止的abort函數(shù)最后一道防線的函數(shù)可以由程序員設置從而規(guī)定其終止前的行為第48頁/共63頁u可以通過set_terminate函數(shù)修改捕捉不住異常的默認處理器,從而使得發(fā)生捉不住異常時,被自定義函數(shù)處理:u void myTerminate()cout“HereIsMyTerminaten”;u set_terminate(myTerminate);u set_terminate函數(shù)在頭文件exception中聲明,參數(shù)為函數(shù)指針void(*)().第49頁/共63頁第50頁/共63頁15.6.1 構造函數(shù)的錯誤處理 構造函數(shù)正象一個封閉的模塊,輸出狀態(tài)是一個新創(chuàng)對象任何創(chuàng)
28、建過程中的錯誤(例如,動態(tài)內(nèi)存申請失敗等)都會導致模板的出乎意料這時候的狀態(tài)是不能接續(xù)后繼操作的,如:捆綁對象的操作因為沒有對象而招致失敗如果敢于正常返回,則又招致荒謬的結果如:第51頁/共63頁class Date int year, month, day; void init(int y, int m, int d) if(y1|m12|d31) return; /? public: Date(int y=2000, int m=1, int d=1) :year(y),month(m),day(d) init(); void print() coutyear“-”month“-” day
29、“n”; ;int main() Date ad(2000,13,1); ad.print(); / 荒謬:月份為13第52頁/共63頁異常操作應該恢復到對類對象進行創(chuàng)建和使用以前的狀態(tài)void f() Date(2000,13,1); ad.print();int main() try f(); /其他操作 catch(out_of_range) cout“對象創(chuàng)建失敗,改換門庭”; class Date int year, month, day; void init(int y, int m, int d) if(y1|m12|d31) throw out_of_range; public
30、: Date(int y=2000, int m=1, int d=1) :year(y),month(m),day(d) init(); void print() coutyear“-”month“-” day“n”; ;第53頁/共63頁 構造函數(shù)沒有返回類型,無法通過返回值來報告運行狀態(tài),所以只通過一種非函數(shù)機制的途徑,即異常機制,來解決構造函數(shù)的出錯問題。第54頁/共63頁15.7.1 另一種循環(huán)控制法 多重循環(huán)控制中,當某個條件滿足時,需要立刻退出所有循環(huán)體時,一般用goto語句反而比較現(xiàn)實否則一重一重地退,編程復雜,性能也連累這是在一個函數(shù)中時的做法 在一個循環(huán)中,遇到一個,當某個
31、條件滿足時,需要立刻退出函數(shù)函數(shù)調(diào)用鏈調(diào)用鏈所在的循環(huán)時,就不能用goto語句,因為不在同一個函數(shù)中此時,程序員渴望函數(shù)能奇跡般地跳躍用異??刂平Y構便能滿足編程要求如圖:第55頁/共63頁圖15-6 另一種控制結構 throw catch try第56頁/共63頁 不能說異常發(fā)生一定是一種錯誤,它已經(jīng)成為了一種有效的控制手段。 異常并非一定是針對錯誤時刻處理。第57頁/共63頁15.7.2 遞歸控制法u遞歸函數(shù)本身就是一個深不可測的調(diào)用鏈,要立刻從調(diào)用鏈中徹底退出,若走函數(shù)控制這條線,那么,必須逐個進行函數(shù)返回u而異常控制可以實現(xiàn)瞬間跳躍u如求解”n皇后問題”,代碼如下:第58頁/共63頁 #include #include #inclu
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年版企業(yè)破產(chǎn)重整合同
- 2024年度無息個人婚禮籌備借款協(xié)議書下載3篇
- 2025年日喀則貨運資格證模擬考試
- 2024年停薪留職期間員工社會保險及福利協(xié)議合同3篇
- 2025購房合同的范本 購房合同樣本
- 2025年柳州貨運從業(yè)資格證考試卷
- 洛陽理工學院《內(nèi)科護理學2》2023-2024學年第一學期期末試卷
- 2024年墓地環(huán)境優(yōu)化協(xié)議3篇
- 汽車俱樂部噴泉建設合同
- 2024年度家電品牌全國巡回展銷合同范本3篇
- 【MOOC】法理學-西南政法大學 中國大學慕課MOOC答案
- 遼寧省普通高中2024-2025學年高一上學期12月聯(lián)合考試語文試題(含答案)
- 儲能運維安全注意事項
- 2024蜀繡行業(yè)市場趨勢分析報告
- 電力法律法規(guī)培訓
- 北京交通大學《成本會計》2023-2024學年第一學期期末試卷
- 2024年世界職業(yè)院校技能大賽“智能網(wǎng)聯(lián)汽車技術組”參考試題庫(含答案)
- 【課件】校園安全系列之警惕“死亡游戲”主題班會課件
- 化工企業(yè)冬季安全生產(chǎn)檢查表格
- 2024年工程勞務分包聯(lián)合協(xié)議
- 蜜雪冰城員工合同模板
評論
0/150
提交評論