1.3 C++異常處理
程序中的錯誤通常包括:語法錯誤、邏輯錯誤和運行時異常(Exception)。其中,語法錯誤通常是指函數、類型、語句、表達式、運算符或標識符的使用不符合C++中的語法,這種錯誤在程序編譯或連接時就會由編譯器指出;邏輯錯誤是指程序能順利運行,但是沒有實現或達到預期的功能或結果,這類錯誤常需要通過調試或測試才能發現;運行時異常是指在程序運行過程中,由于意外事件的發生而導致程序異常終止,如內存空間不足、打開的文件不存在、零除數、下標越界等。
異常或錯誤的處理方法有很多,如判斷函數返回值、使用全局的標志變量、直接使用C++中的exit()或abort()函數來中斷程序的執行。
1.3.1 使用C++異常處理
程序運行時異常的產生雖然無法避免,但可以預料。為了保證程序的健壯性,必須在程序中對運行時異常進行預見性處理,這種處理稱為異常處理。
C++提供了專門用于異常處理的一種結構化形式的描述機制try/throw/catch。該異常處理機制能夠把程序的正常處理和異常處理邏輯分開表示,使得程序的異常處理結構清晰,通過異常集中處理的方式,解決異常處理的問題。
1.try語句塊
try語句塊的作用是啟動異常處理機制,偵測try語句塊中的程序語句執行時可能產生的異常。如有異常產生,則拋出異常。try的格式如下:

注意:try總是與catch一同出現,在一個try語句塊之后,至少應該有一個catch語句塊。
2.catch語句塊
catch語句塊用來捕捉try語句塊產生的異常或用throw拋出的異常,然后進行處理,其格式如下:

其中,catch中的形參類型可以是C++基本類型(如int、long、char等)、構造類型,還可以是一個已定義的類的類型,包括類的指針或者引用類型等。如果在catch中指定了形參名,則可像一個函數的參數傳遞那樣將異常值傳入,并可在catch語句塊中使用該形參名。例如:
try { throw "除數不能為0!"; } catch(const char * s) // 指定異常形參名 { cout<<s<<endl; // 使用異常形參名 }
注意:
(1) 當catch中的整個形參為“…”時,則表示catch能捕捉任何類型的異常。
(2) catch前面必須是try語句塊或另一個catch塊。正因如此,在書寫代碼時應使用這樣的格式:
try { … } catch(…) { … } catch(…) { … }
3.throw
throw用來強行拋出異常,其格式如下:

其中,異常類型表達式可以是類對象、常量或變量表達式等。
4.三者關系和注意點
throw和catch的關系就好比函數調用關系,catch指定形參,而throw給出實參。編譯器將按照catch出現的順序及catch指定的參數類型確定throw拋出的異常應該由哪個catch來處理。
throw不一定出現在try語句塊中,實際上,它可以出現在任何需要的地方,即使在catch中的語句塊中,仍然可以繼續使用throw,只要最終有catch可以捕獲它即可。例如:
class Overflow { // ... public: Overflow(char,double,double); }; void f(double x) { // ... throw Overflow('+',x,3.45e107);// 在函數體中使用throw,用來拋出一個對象 } try { // ... f(1.2); // ... } catch(Overflow& oo) { // 處理Overflow類型的異常 }
當throw出現在catch語句塊中時,通過throw既可以重新拋出一個新類型的異常,也可以重新拋出當前這個異常,在這種情況下,throw不應帶任何實參。例如:
try { ... } catch(int) { throw "hello exception"; // 拋出一個新的異常,異常類型為const char* } catch(float) { throw; // 重新拋出當前的float類型異常 }
1.3.2 嵌套異常和棧展開
在C++中,異常處理嵌套一般指下列結構:

當然,在程序代碼中,若一個函數中的異常處理語句塊中還有另一個函數的調用,而另一個函數本身也會產生異常,這樣,通過函數嵌套調用也會形成異常處理嵌套。
在嵌套異常情況下,最底層函數所拋出的異常首先在內層中依次查找相匹配的catch語句塊,只要遇到第一個匹配的catch子句,查找就會結束,然后進入該catch子句,進行處理。若沒有匹配,則內層函數產生的異常逐層向外傳遞,最后回到主程序中。這種因發生異常而逐步退出復合語句或函數定義的過程,稱為棧展開(stack unwinding)過程。
需要說明的是:
(1) 隨著棧展開,在退出的復合語句和函數定義中聲明的局部變量的生命期也結束了。在棧中分配的局部變量所占用的資源也被釋放,由系統回收。但是,如果函數動態分配過內存或其他資源(包括用new運算符取得的資源和打開的文件)出現異常,這些資源的釋放語句可能被忽略,從而造成這些資源將永遠不會自動釋放。
(2) 在棧展開期間,當一個復合語句(或語句塊)或函數退出時,若遇到的局部變量是類對象時,則棧展開過程將自動調用該對象的析構函數,完成資源的釋放。
- JavaScript修煉之道
- GeoServer Cookbook
- 潮流:UI設計必修課
- Vue.js 3.x從入門到精通(視頻教學版)
- RTC程序設計:實時音視頻權威指南
- Learning ArcGIS Pro
- Jupyter數據科學實戰
- Asynchronous Android Programming(Second Edition)
- 利用Python進行數據分析
- 基于SpringBoot實現:Java分布式中間件開發入門與實戰
- SQL Server 2008 R2數據庫技術及應用(第3版)
- Hands-On JavaScript for Python Developers
- AI自動化測試:技術原理、平臺搭建與工程實踐
- Raspberry Pi Blueprints
- Monitoring Docker