Unix/Linux C++應用開發-異常以及錯誤處理

計算機應用程序中離不開錯誤處理,尤其是生產型大型軟件系統。應用軟件系統運行屬於循環處理事務,出錯後需要保證不能讓軟件程序直接退出。這就需要使用一定的程序容錯處理來應對。一般情況下,大型軟件開發中的軟件系統容錯處理會結合異常處理、錯誤代碼定義的使用與相應的出錯處理日誌記錄,包括一定的參與大型生產系統的監控系統等配合保障系統的穩定性。下面本章將會就C++軟件系統中提供的異常處理作詳細的講述,包括基本概念以及應用操作情況。

Unix/Linux C++應用開發-異常以及錯誤處理

一 、C++異常處理簡介

軟件應用程序中,異常處理機制是一種比較有效的處理系統運行時錯誤的方法。C++針對異常處理提供了一種標準的方法,用於處理軟件程序運行時的錯誤,並用於處理軟件系統中可預知或不可預知的問題。這樣就可以保證軟件系統運行的穩定性與健壯性。

C++中異常的處理主要用於針對程序在運行時刻出現錯誤而提供的語言層面的保護機制。它允許開發者最大限度地發揮,針對異常處理進行相應的修改調整。

C++應用程序中在考慮異常設計時,並不是所有的程序模塊處理都需要附加上異常情況的處理。相對來說,異常處理沒有普通方法函數調用速度快。過度的錯誤處理會影響應用程序運行的效率。通常在C++開發的軟件系統中,應用程序都由對應的庫、組件以及運行的具體不同模塊組成。在設計時,異常的處理應充分考慮到獨立程序庫以及組件之間的情況。便於使用者在程序出現異常情況下,使用庫或者組件的開發者能夠快速定位出庫、組件還是應用程序的錯誤。

由於大型軟件系統中庫與組件通常都是以獨立的程序方式提供給具體設計開發模塊人員。開發人員通常不瞭解組件與庫中具體實現情況,只是根據提供的接口來操作使用。在不清楚具體庫與組件的具體實現情況下,開發者可能在極端的情況下操作應用庫或者組件提供的方法。此時相應的異常處理通信就會成為必不可少的錯誤處理通信機制。下面將會就C++語言提供的異常機制支持作詳細應用講述,在實踐中總結異常應用的場合與具體實現方法。

二、C++異常處理方法

C++語言異常部分的支持也是在後續語言發展中逐步討論並添加的內容。該語言針對異常處理提供了一系列的語法支持。C++語言除了提供異常的關鍵字語法支持以外,其標準庫中支持異常處理而封裝異常類也很好的為應用程序中異常處理判斷使用提供直接的幫助。

2.1 C++異常處理:try、throw與catch

C++語言中針對異常處理提供了三個關鍵字,分別為try、throw與catch。C++應用程序中通過這三個關鍵字實現機制組合來實現異常的處理。下面通過常見異常處理操作方式來介紹應用程序中使用異常處理關鍵字的基本方式,其基本語法定義如下所示。

try

{

… //可能出錯產生異常的代碼

throwtypen();

}catch(type1)

{

… //對應類型的異常處理代碼

}catch(type2)

{

… //對應類型的異常處理代碼

}

… //更多類型異常處理代碼

或者

try

{

function();//調用可能會拋出異常的函數方法

}catch(type1)

{

… //對應類型的異常處理代碼

}catch(type2)

{

… //對應類型的異常處理代碼

}

C++應用程序中,try關鍵字後的代碼塊中通常放入可能出現異常的代碼。隨後的catch塊則可以是一個或者多個;catch塊主要用於異常對應類型的處理。try塊中代碼出現異常可能會對應多種異常處理情況,catch關鍵字後的圓括號中則包含著對應類型的參數。

try塊中代碼體作為應用程序遵循正常流程執行。一旦該代碼體中出現異常操作,會根據操作的判斷拋出對應的異常類型。隨後逐步的遍歷catch代碼塊,此步驟與switch控制結構有點相像。當遍歷到對應類型catch塊時,代碼會跳轉到對應的異常處理中執行。如果try塊中代碼沒有拋出異常,則程序繼續執行下去。

try體中可以直接拋出異常,或者在try體中調用的函數體中間接的拋出。下面將會通過一個簡單的異常處理實例,來使用C++語言中支持的異常處理方式。

1.準備實例

打開UE工具,創建新的空文件,並且另存為chapter1401.cpp。該代碼文件隨後會同makefile文件一起通過FTP工具傳輸至Linux服務器端,客戶端通過scrt工具訪問操作。程序代碼文件編輯如下所示。

/**

* 實例chapter1401

* 源文件chapter1401.cpp

* 簡單異常處理

*/

#include <iostream>

#include <string>

using namespace std;

int main()

{

try

{

intvalue1,value2; //定義兩個整型變量

cout<

cin>>value1>>value2; //從鍵盤輸入兩個整型數

cout<

if(value2== 0) //如果除數為0則拋出異常

{

throw0;

}

else //否則直接計算相除操作

{

cout<

}

}catch(inti) //捕捉參數為整型的異常

{

cout<

}

return0;

}

本實例程序主要通過try…catch方式來捕獲異常,演示簡單容錯處理的一般實現方法。程序主要在main()函數內部定義實現,具體程序剖析見程序註釋與後面講解。

Linux平臺下需要編譯源文件為chapter1401.cpp,相關makefile工程文件編譯命令編輯如下所示。

OBJECTS=chapter1401.o

CC=g++

chapter1401: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1401

clean:

rm-f chapter1401 core $(OBJECTS)

submit:

cp-f -r chapter1401 ../bin

cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替換了代碼文件、程序編譯中間文件、可執行程序等。在編譯命令部分-g選項的加入,表明程序編譯同時加入了可調式信息。

3.編譯運行程序

當前shell下執行make命令,生成可執行程序文件。隨後,通過make submit命令提交程序文件至本實例bin目錄。通過cd命令定位至實例bin目錄,執行該程序文件運行,結果如下所示。

[developer@localhost src]$ make

g++ -c-o chapter1401.o chapter1401.cpp

g++ chapter1401.o -g -o chapter1401

[developer @localhost src]$ make submit

cp -f -r chapter1401 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter1401

Please input two value:

12

0

Maybe exception code:

divisor is 0!

本實例中主要演示了除法中除數為0時拋出相應的異常,並作出對應的處理。本實例中try塊中主要放置著可能出現異常的代碼。該代碼塊中首先定義兩個整型變量value1與value2。隨後提示從鍵盤輸入兩個整型數,接著輸出可能出現異常的提示信息。最後判斷輸入的除數value2是否為0。如果該判斷條件成立,即程序執行中發生了除數為0的錯誤,則對應的代碼體中使用throw拋出異常。如果判斷條件不成立,則程序繼續執行下去,

一旦符合拋出異常條件成立,則執行相應代碼採用throw關鍵字拋出異常。此時關鍵字後跟著一個整型數0表示該異常。隨後catch語句塊捕捉異常,由於catch隨後的參數類型符合拋出異常的類型匹配則執行catch中的異常處理,即打印輸出除數為0的提示信息。在主程序中try塊中拋出的異常的類型需要與隨後的catch中參數類型一致,否則會程序將會異常終止。初學者可以將上述catch中參數類型修改為double類型測試看看當前編譯器針對異常發生後匹配不到對應的catch時給出的反應。

如果try塊中的代碼沒有拋出異常,則程序會直接忽略隨後的catch塊,直接執行後續的代碼處理。在設計程序可能出現的異常處理時,將拋出異常的類型與對應的catch塊中的類型規範的對應起來有助於程序在異常處理加入後更加清晰有條理。

2.2 throw拋出異常處理

C++程序中異常拋出是採用關鍵字throw實現的。通常throw關鍵字後會跟隨著一個操作數,該操作數可以是一個表達式、一個C++內置類型數據或者為類類型的對象等。最常見的異常拋出都會放在try代碼塊中。當然,C++也允許拋出異常的地方在函數中供try塊中的代碼調用。正常情況下異常拋出點的定義不要放在try塊外部無關的地方,因為那樣通常會引起編譯器默認的終止程序處理。最好的情況下,異常拋出點直接或者間接的定義在try塊中。

try塊中可以包含一個或者多個異常拋出點。但是需要注意的是,異常只要一拋出,對應的catch塊捕捉到後,該try塊中以下的代碼體執行會被終止。代碼執行直接進入對應的catch塊中,最後catch塊執行處理完異常後直接跳轉至所有當前try塊對應的catch塊之後。

下面通過一個具體的實例來演示throw異常拋出的基本操作與需要注意的使用情況。

1.準備實例

打開UE工具,創建新的空文件並且另存為chapter1402.cpp。該代碼文件隨後會同makefile文件一起通過FTP工具傳輸至Linux服務器端,客戶端通過scrt工具訪問操作。程序代碼文件編輯如下所示。

/**

* 實例chapter1402

* 源文件chapter1402.cpp

* 簡單異常處理

*/

#include <iostream>

#include <string>

using namespace std;

void divideCompute(int value1,int value2) //除法計算函數定義

{

if(value1== 0) //如果被除數為0,則拋出參數類型為浮點型異常

{

throw0.0;

}

elseif(value2 == 0) //如果除數為0,則拋出參數類型為整型異常

{

throw0;

}

else //否則執行除法運算並打印輸出結果

{

cout<

}

}

int main()

{

try

{

intvalue1,value2; //定義兩個整型變量

cout<

cin>>value1>>value2; //輸入兩個整型數

cout<

divideCompute(value1,value2); //調用除法計算函數,傳入兩個輸入的整型參數

}catch(inti) //捕捉整型參數異常

{

cout<

}catch(doublei) //捕捉浮點型參數異常

{

cout<

}

cout<

return0;

}

本實例通過throw關鍵字演示在函數內部拋出異常,程序調用處捕獲異常並處理的場景。程序主要由函數divideCompute與主函數main組成,具體程序剖析見程序註釋與後面講解。

Linux平臺下需要編譯源文件為chapter1402.cpp,相關makefile工程文件編譯命令編輯如下所示。

OBJECTS=chapter1402.o

CC=g++

chapter1402: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1402

clean:

rm-f chapter1402 core $(OBJECTS)

submit:

cp-f -r chapter1402 ../bin

cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替換了代碼文件、程序編譯中間文件、可執行程序等。在編譯命令部分-g選項的加入,表明程序編譯同時加入了可調式信息。

3.編譯運行程序

當前shell下執行make命令,生成可執行程序文件。隨後通過make submit命令提交程序文件至本實例bin目錄。通過cd命令定位至實例bin目錄,執行該程序文件運行結果如下所示。

[developer@localhost src]$ make

g++ -c-o chapter1402.o chapter1402.cpp

g++ chapter1402.o -g -o chapter1402

[developer @localhost src]$ make submit

cp -f -r chapter1402 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter1402

Please input two value:

0

0

Maybe exception code:

dividend is 0!

here!

本實例中主要設計了除法中可能出現的異常。此處拋出異常沒有直接放在try代碼塊中,而是通過間接的方式放置在函數定義中,供try代碼塊中調用。此時除法操作定義的函數中包含著可能出現的兩個異常拋出點。一類是針對被除數為0時,拋出浮點型參數異常。另一類則針對除數為0時,拋出整型參數異常。

整個主程序代碼中catch主要有兩個參數類型的捕獲處理。分別對應著除數以及被除數為0時異常拋出捕獲處理。上述實例表明try代碼塊中可以直接放置或者間接放置多個throw異常拋出點。一旦其中一個異常點拋出後程序控制權將會轉移到對應的catch參數類型異常處理中。對應的異常處理完後將會意味著程序執行將會終止try異常塊中的程序塊。直接轉移到對應的所有catch異常處理塊之後的代碼執行。

上述主程序中從try塊中開始執行,try代碼快中首先定義兩個整型變量value1與value2。隨後提示從鍵盤輸入兩個整型數,上述程序執行後輸入兩個整型數都為0。之後將輸入的兩個整型數作為除法計算函數的實參調用除法計算函數。根據傳入的實參首先判斷被除數為0則拋出浮點型參數異常,隨後程序轉移到對應catch塊中執行異常處理,輸出提示信息“dividend is 0!”。程序代碼轉移至catch處理塊中之後,則退出了異常拋出的所在塊。直接處理完畢後,轉到所有catch塊之後繼續執行代碼,即最後執行輸出“here!”提示信息代碼。

2.3 catch捕捉異常處理

catch關鍵字的代碼塊則主要負責異常的捕獲處理,catch塊可以擁有多個,並且緊跟著try塊之後。每個catch塊部分主要由圓括號中參數類型,緊跟其後的大括號中異常代碼處理部分構成。前面小節在講述異常拋出處理時已經對其使用的原理作過解釋,即當在可能拋出異常的try代碼塊中通過throw拋出了異常。隨後開始匹配catch塊中的參數類型是否符合異常拋出時的類型,如果符合則執行對應catch塊中的代碼處理。

多個catch塊中,遍歷到對應的catch塊則執行隨後代碼塊處理異常。catch塊中隨後圓括號中參數類型,該類型可以擁有變量名稱或者對象名稱,也可以直接是對應的參數類型。如果是對應的類型對象,則在catch塊中可以引用該對象處理。而如果是隻有數據類型沒有相應參數名稱的參數,則直接匹配對應的參數類型即可,程序會從try塊中轉移至catch塊中執行。

如果異常拋出了,匹配對應的catch塊沒有對應類型的異常處理時,則會調用默認的異常abort來終止程序的執行。catch塊中的參數除了採用基本的內置類型、自定義類類型外,還可以使用三個點的省略號(即catch(…))來表示捕捉所有的異常。此時,當try塊中一旦拋出異常時,如果對應的catch塊中只有一個catch(…)或者對應的catch(…)放置在所有其它catch塊最後。那麼,異常發生時只會執行對應的catch(…)中的代碼處理,或者當前面所有的catch都匹配不到時,則匹配catch(…)捕獲所有異常處理。通常情況下捕捉所有異常的定義會放在所有其它catch塊最後位置,這樣的好處就是程序不會因為拋出異常匹配不到對應的catch塊而終止。

上述實例代碼作出如下修改,根據實例中添加catch(…)塊觀察捕捉異常處理情況。

1.準備實例

打開UE工具,創建新的空文件並且另存為chapter1403.cpp。該代碼文件隨後會同makefile文件一起通過FTP工具傳輸至Linux服務器端,客戶端通過scrt工具訪問操作。程序代碼文件編輯如下所示。

/**

* 實例chapter1403

* 源文件chapter1403.cpp

* 捕獲所有異常處理

*/

#include <iostream>

#include <string>

using namespace std;

void divideCompute(int value1,int value2) //除法計算函數定義

{

if(value2== 0) //如果除數為0,則拋出相應的整型異常

{

throw0;

}

else //否則計算除法運算,並打印結果

{

cout<

}

}

int main()

{

try

{

intvalue1,value2; //定義兩個整型變量

cout<

cin>>value1>>value2; //從鍵盤輸入兩個整型值

cout<

divideCompute(value1,value2); //調用除法計算函數

}catch(doublei) //對應浮點型參數異常處理

{

cout<

}catch(inti) //對應整型參數異常處理

{

cout<

}catch(...) //捕獲所有異常處理

{

cout<

}

cout<

return0;

}

本實例主要通過catch(…)方式使用演示在異常處理相關類型捕獲不到的情況下,異常的默認處理情況。程序主要由函數divideCompute()與主函數main()組成,程序具體剖析見程序註釋與後面程序講解。

Linux平臺下需要編譯源文件為chapter1403.cpp,相關makefile工程文件編譯命令編輯如下所示。

OBJECTS=chapter1403.o

CC=g++

chapter1403: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1403

clean:

rm-f chapter1403 core $(OBJECTS)

submit:

cp-f -r chapter1403 ../bin

cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替換了代碼文件、程序編譯中間文件、可執行程序等。在編譯命令部分-g選項的加入,表明程序編譯同時加入了可調式信息。

3.編譯運行程序

當前shell下執行make命令,生成可執行程序文件,隨後通過make submit命令提交程序文件至本實例bin目錄,通過cd命令定位至實例bin目錄,執行該程序文件運行結果如下所示。

[developer@localhost src]$ make

g++ -c-o chapter1403.o chapter1403.cpp

g++ chapter1403.o -g -o chapter1403

[developer @localhost src]$ make submit

cp -f -r chapter1403 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter1403

Please input two value:

1

0

Maybe exception code:

divisor is 0!

here!

上述實例中,主程序中根據輸入的實參整型數調用除法計算函數。在除法計算函數中,一旦除數為0,則拋出整型參數為0的異常。此時開始匹配try塊對應後的catch塊中的參數類型。首先對比第一個catch塊,由於其參數類型為double類型不符合類型匹配,則繼續向下搜索。try塊後的第二個捕捉異常處理catch塊中的參數類型為整型,並且該參數還擁有參數類型對應變量。匹配到該catch塊後,程序轉移到對應的代碼塊中處理,該代碼塊中主要用於打印輸出說明除數為0,此時則直接引用了參數類型後的變量名。

主程序中上述匹配到的catch塊執行完之後直接轉移至所有catch塊之後繼續執行。執行最後打印輸出語句。此時如果前面幾個catch都沒有匹配對應的參數類型時,則catch(…)塊中異常處理代碼會被執行,初學者可以自行修改程序進行測試學習。

2.4 throw重拋異常處理

重新拋出異常通常應用在try嵌套層中。最常見的應用即當前try塊中捕捉異常塊不作任何處理時,可以通過重拋異常讓try層外部的catch塊來處理。如果try層外部已經沒有對應的try與catch塊處理,則會調用默認的異常處理終止程序。下面將會通過一個完整實例來演示異常重拋的應用情況。

1.準備實例

打開UE工具,創建新的空文件並且另存為chapter1404.cpp。該代碼文件隨後會同makefile文件一起通過FTP工具傳輸至Linux服務器端,客戶端通過scrt工具訪問操作。程序代碼文件編輯如下所示。

/**

* 實例chapter1404

* 源文件chapter1404.cpp

* throw重拋異常處理

*/

#include <iostream>

#include <string>

using namespace std;

void divideCompute(int value1,int value2)

{

try

{

if(value2== 0) //如果除數為0,則拋出異常

{

throw0; //拋出整型異常

}

else //否則計算除法運算

{

cout<

}

}catch(doublei) //捕捉浮點型參數異常

{

cout<

}catch(inti) //捕捉整型參數異常

{

cout<

throw; //重拋異常

}catch(...) //捕捉所有類型異常

{

cout<

}

}

int main()

{

try

{

intvalue1,value2; //定義兩個整型變量

cout<

cin>>value1>>value2; //輸入兩個整型變量

cout<

divideCompute(value1,value2); //調用除法計算函數

}catch(doublei) //外層捕捉浮點型異常

{

cout<

}catch(inti) //外層捕捉整型異常

{

cout<

}catch(...) //外層捕捉所有類型異常

{

cout<

}

cout<

return0;

}

本實例程序主要演示try…catch嵌套使用中,通過throw關鍵字重新拋出異常的功能。程序主要由函數divideCompute與主函數組成,程序具體剖析見程序註釋與後面講解。

Linux平臺下需要編譯源文件為chapter1404.cpp,相關makefile工程文件編譯命令編輯如下所示。

OBJECTS=chapter1404.o

CC=g++

chapter1404: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1404

clean:

rm-f chapter1404 core $(OBJECTS)

submit:

cp-f -r chapter1404 ../bin

cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替換了代碼文件、程序編譯中間文件、可執行程序等。在編譯命令部分-g選項的加入,表明程序編譯同時加入了可調式信息。

3.編譯運行程序

當前shell下執行make命令,生成可執行程序文件。隨後通過make submit命令提交程序文件至本實例bin目錄。通過cd命令定位至實例bin目錄,執行該程序文件運行結果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter1404.o chapter1404.cpp

g++ chapter1404.o -g -o chapter1404

[developer@localhost src]$ make submit

cp -f -r chapter1404 ../bin

cp -f -r *.h ../include

[developer@localhost src]$ cd ../bin

[developer@localhost bin]$ ./chapter1404

Please input two value:

1

0

Maybe exception code:

rethrow exception:

divisor is 0!

here!

本實例中主要演示了try塊中調用除法計算方法。該方法中又存在try與catch塊的嵌套處理情況。上述實例主程序中同樣是定義兩個整型變量value1與value2,隨後從鍵盤輸入兩個整型數作為實參調用除法計算函數。除法計算函數中仍然包含著try塊,try塊中包含著可能拋出異常的代碼。一旦輸入的實參中除數為0則報出整型類型的異常,被隨後的catch整型參數塊所捕獲執行相對應的異常處理代碼。

在對應的異常處理catch塊中允許針對異常不作出任何的處理,而直接使用throw不帶任何的參數類型重新拋出異常,交由外層的catch塊來捕獲。此時代碼執行被轉移至外層catch塊進行匹配。如果匹配到整型類型的異常處理時,則進入執行相應的異常處理。如果沒有匹配到則執行catch(…)塊捕獲所有異常,隨後轉移至catch塊外繼續執行。

上述實例程序執行時傳入兩個整型數1和0。由於除數為0則在除法函數體中try塊拋出異常。此時最內一層catch開始進行捕獲匹配,匹配到整型參數類型異常處理則進入執行。最內層的catch整型參數中除了打印輸出提示重拋異常信息外,則執行throw重拋異常。被外層對應的catch塊捕獲進入執行相應異常處理,通過直接引用捕獲異常參數類型的變量打印除數為0的信息提示。

三、小結

C++異常處理的提出為編寫出更具健壯性、穩定性的應用程序提供了很好的支持。但是異常與前面介紹該語言的特性一樣,不能夠濫用。因為任何支持的特性總是以犧牲一定的效率來實現的。通過上述介紹,用戶在實際軟件系統中可以結合錯誤處理、異常以及對應的錯誤日誌等,最終開發出高穩定、高容錯性的應用系統。由於篇幅有限,未完待更。。。

注:需要C/C++ Linux服務器開發學習資料私信“資料”(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等),免費分享


分享到:


相關文章: