計算機應用程序中離不開錯誤處理,尤其是生產型大型軟件系統。應用軟件系統運行屬于循環處理事務,出錯后需要保證不能讓軟件程序直接退出。這就需要使用一定的程序容錯處理來應對。一般情況下,大型軟件開發中的軟件系統容錯處理會結合異常處理、錯誤代碼定義的使用與相應的出錯處理日志記錄,包括一定的參與大型生產系統的監控系統等配合保障系統的穩定性。下面本章將會就C++軟件系統中提供的異常處理作詳細的講述,包括基本概念以及應用操作情況。
14.1??C++異常處理簡介
軟件應用程序中,異常處理機制是一種比較有效的處理系統運行時錯誤的方法。C++針對異常處理提供了一種標準的方法,用于處理軟件程序運行時的錯誤,并用于處理軟件系統中可預知或不可預知的問題。這樣就可以保證軟件系統運行的穩定性與健壯性。
C++中異常的處理主要用于針對程序在運行時刻出現錯誤而提供的語言層面的保護機制。它允許開發者最大限度地發揮,針對異常處理進行相應的修改調整。
C++應用程序中在考慮異常設計時,并不是所有的程序模塊處理都需要附加上異常情況的處理。相對來說,異常處理沒有普通方法函數調用速度快。過度的錯誤處理會影響應用程序運行的效率。通常在C++開發的軟件系統中,應用程序都由對應的庫、組件以及運行的具體不同模塊組成。在設計時,異常的處理應充分考慮到獨立程序庫以及組件之間的情況。便于使用者在程序出現異常情況下,使用庫或者組件的開發者能夠快速定位出庫、組件還是應用程序的錯誤。
由于大型軟件系統中庫與組件通常都是以獨立的程序方式提供給具體設計開發模塊人員。開發人員通常不了解組件與庫中具體實現情況,只是根據提供的接口來操作使用。在不清楚具體庫與組件的具體實現情況下,開發者可能在極端的情況下操作應用庫或者組件提供的方法。此時相應的異常處理通信就會成為必不可少的錯誤處理通信機制。下面將會就C++語言提供的異常機制支持作詳細應用講述,在實踐中總結異常應用的場合與具體實現方法。
14.2? C++異常處理方法
C++語言異常部分的支持也是在后續語言發展中逐步討論并添加的內容。該語言針對異常處理提供了一系列的語法支持。C++語言除了提供異常的關鍵字語法支持以外,其標準庫中支持異常處理而封裝異常類也很好的為應用程序中異常處理判斷使用提供直接的幫助。
14.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<<"Pleaseinput two value:"<<endl;??????????????????????????????? //提示輸入信息
?????????????????? cin>>value1>>value2;????????????????????????????????????????????????????????????? //從鍵盤輸入兩個整型數
?????????????????? cout<<"Maybeexception code:"<<endl;??????????????????????????????? //提示可能出現異常的代碼信息
?????????????????? if(value2== 0)??????????????????????????????????????????????????????????????????????????? //如果除數為0則拋出異常
?????????????????? {
??????????????????????????? throw 0;
?????????????????? }
?????????????????? else?????????????????????????????????????????????????????????????????????????????????????????????? //否則直接計算相除操作
?????????????????? {
??????????????????????????? cout<<"value1/value2:"<<(value1/value2)<<endl;
?????????????????? }
???????? }
catch(inti)??????????????????????????????????????????????????????????????????????????????????????????? //捕捉參數為整型的異常
???????? {
?????????????????? cout<<"divisoris 0!"<<endl;????????????????????????????????????????????????????? //異常處理代碼
???????? }
???????? return0;
}
本實例程序主要通過try…catch方式來捕獲異常,演示簡單容錯處理的一般實現方法。程序主要在main()函數內部定義實現,具體程序剖析見程序注釋與后面講解。
2.編輯makefile
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塊中的類型規范的對應起來有助于程序在異常處理加入后更加清晰有條理。
14.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<<"value1/value2:"<<(value1/value2)<<endl;
???????? }
}
int main()
{
???????? try
???????? {
?????????????????? intvalue1,value2;??????????????????????????????????????????????????? //定義兩個整型變量
?????????????????? cout<<"Pleaseinput two value:"<<endl;???????????? //提示輸入兩個整型數信息
?????????????????? cin>>value1>>value2;??????????????????????????????????????????? //輸入兩個整型數
?????????????????? cout<<"Maybeexception code:"<<endl;
?????????????????? divideCompute(value1,value2);??????????????????????????????????? //調用除法計算函數,傳入兩個輸入的整型參數
???????? }
catch(inti)???????????????????????????????????????????????????????????????????????? //捕捉整型參數異常
???????? {
?????????????????? cout<<"divisoris 0!"<<endl;?????????????????????????????????? //對應的異常處理
???????? }
catch(doublei)????????????????????????????????????????????????????????????????????????? //捕捉浮點型參數異常
???????? {
?????????????????? cout<<"dividendis 0!"<<endl;?????????????????????????????? //對應的異常處理
???????? }
???????? cout<<"here!"<<endl;?????????????????????????????????????????????????????? //異常處理塊后的正常代碼
???????? return 0;
}
本實例通過throw關鍵字演示在函數內部拋出異常,程序調用處捕獲異常并處理的場景。程序主要由函數divideCompute與主函數main組成,具體程序剖析見程序注釋與后面講解。
2.編輯makefile
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!”提示信息代碼。
14.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(...),類似switch中的case1、case2、...default】通常情況下捕捉所有異常的定義會放在所有其它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<<"value1/value2:"<<(value1/value2)<<endl;
???????? }
}
int main()
{
???????? try
???????? {
?????????????????? intvalue1,value2;??????????????????????????????????????????????????? ???????? //定義兩個整型變量
?????????????????? cout<<"Pleaseinput two value:"<<endl;
?????????????????? cin>>value1>>value2;??????????????????????????????????????????? ???????? //從鍵盤輸入兩個整型值
?????????????????? cout<<"Maybeexception code:"<<endl;
?????????????????? divideCompute(value1,value2);??????????????????????????????????? ???????? //調用除法計算函數
???????? }
catch(doublei)????????????????????????????????????????????????????????????????????????? ???????? //對應浮點型參數異常處理
???????? {
?????????????????? cout<<"doubleexception!"<<endl;
???????? }
catch(inti)???????????????????????????????????????????????????????????????????????? ???????? //對應整型參數異常處理
???????? {
?????????????????? cout<<"divisoris "<<i<<”!”<<endl;
???????? }
catch(...)???????????????????????????????????????????????????????????????????????????? ???????? //捕獲所有異常處理 ?【前面的catch沒有捕獲到,則在這里捕獲】
???????? {
?????????????????? cout<<"allexception!"<<endl;
???????? }
???????? cout<<"here!"<<endl;
???????? return0;
}
本實例主要通過catch(…)方式使用演示在異常處理相關類型捕獲不到的情況下,異常的默認處理情況。程序主要由函數divideCompute()與主函數main()組成,程序具體剖析見程序注釋與后面程序講解。
2.編輯makefile
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(…)塊中異常處理代碼會被執行,初學者可以自行修改程序進行測試學習。
14.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,則拋出異常
?????????????????? {
??????????????????????????? throw 0;?????????????????????????????????????????????????? //拋出整型異常
?????????????????? }
?????????????????? else?????????????????????????????????????????????????????????????????? //否則計算除法運算
?????????????????? {
??????????????????????????? cout<<"value1/value2:"<<(value1/value2)<<endl;
?????????????????? }
???????? }
catch(doublei)???????????????????????????????????????????????????????????????? //捕捉浮點型參數異常
???????? {
?????????????????? cout<<"doubleexception!"<<endl;
???????? }catch(inti)??????????????????????????????????????????????????????????????? //捕捉整型參數異常
???????? {
?????????????????? cout<<"rethrowexception:"<<endl;
?????????????????? throw;??????????????????????????????????????????????????????????????? //重拋異常
???????? }catch(...)?????????????????????????????????????????????????????????????????? //捕捉所有類型異常
???????? {
?????????????????? cout<<"allexception!"<<endl;
???????? }
}
int main()
{
???????? try
???????? {
?????????????????? intvalue1,value2;?????????????????????????????????????????? //定義兩個整型變量
?????????????????? cout<<"Pleaseinput two value:"<<endl;
?????????????????? cin>>value1>>value2;????????????????????????????????? //輸入兩個整型變量
?????????????????? cout<<"Maybeexception code:"<<endl;
?????????????????? divideCompute(value1,value2);?????????????????????????? //調用除法計算函數
???????? }
catch(doublei)???????????????????????????????????????????????????????????????? //外層捕捉浮點型異常
???????? {
?????????????????? cout<<"doubleexception!"<<endl;
???????? }catch(inti)??????????????????????????????????????????????????????????????? //外層捕捉整型異常
???????? {
?????????????????? cout<<"divisoris "<<i<<"!"<<endl;
???????? }catch(...)?????????????????????????????????????????????????????????????????? //外層捕捉所有類型異常
???????? {
?????????????????? cout<<"allexception!"<<endl;
???????? }
???????? cout<<"here!"<<endl;
???????? return0;
}
本實例程序主要演示try…catch嵌套使用中,通過throw關鍵字重新拋出異常的功能。程序主要由函數divideCompute與主函數組成,程序具體剖析見程序注釋與后面講解。
2.編輯makefile
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的信息提示。
14.3? C++類體系中異常處理
C++中異常機制同樣可以應用于自定義類類型。在C++程序中,大部分的異常處理都是應用于自定義類類型。通常情況下定義處理異常的類,然后再創建一個專門用于描述對應異常錯誤的對象實例。最后在應用程序中利用這個異常類對象實例處理應用的代碼中可能發生的異常錯誤。
14.3.1??類類型中異常基本應用
類體系中異常的處理情況其實與函數中大致相仿。但由于自定義類型加入了封裝、繼承等特性,其異常處理變得稍微復雜。通常情況下,大型的軟件系統中往往會自定義異常類,供其它模塊程序包含使用。下面將講解一個自定義異常類以及在類中使用該異常類的實例,讀者可以了解異常在類體系中的應用情況與需要注意的問題。
1.準備實例
打開UE工具,創建新的空文件并且另存為chapter1405_01.h、chapter1405_02.h與chapter1405_01.cpp。該代碼文件隨后會同makefile文件一起通過FTP工具傳輸至Linux服務器端,客戶端通過scrt工具訪問操作。程序代碼文件編輯如下所示。
/**
*?實例chapter1405
*?源文件chapter1405_01.h chapter1405_02.h chapter1405_02.cpp
*?異常類的封裝
*/
#ifndef EXCEPTION_H_
#define EXCEPTION_H_
#include <iostream>
#include <string>
using namespace std;
//自定義異常類
class MyException
{
???????? public:
?????????????????? //異常類無參數默認構造函數定義
?????????????????? MyException()????????????????????????????????????????????????
?????????????????? {
??????????????????????????? m_context= "";????????????????????????????????????? //初始化異常信息數據成員為空
?????????????????? }
?????????????????? //異常類空析構函數定義
?????????????????? ~MyException(){}
?????????????????? //異常類字符串類型參數構造函數定義
?????????????????? MyException(const string & right)
?????????????????? {
??????????????????????????? m_context= right;???????????????????????????????? //初始化異常信息數據為字符串對象參數
?????????????????? }
?????????????????? //異常類字符串指針類型參數構造函數定義
?????????????????? MyException(const char * right)
?????????????????? {
??????????????????????????? m_context= right;???????????????????????????????? //初始化異常信息數據為字符串指針參數
?????????????????? }
??? ?????????? //異常類返回異常信息接口方法定義
?????????????????? stringwhat() const
?????????????????? {
??????????????????????????? return m_context;????????????????????????????????? //直接返回相應的異常信息數據成員
?????????????????? }
???????? private:
?????????????????? string?? m_context;?????????????????????????????????????? //字符串類型數據成員,表示異常信息
};
#endif
//異常測試類,除法計算類頭文件chapter1405_02.h
#include "chapter1405_01.h"
//自定義除法計算類
class Compute
{
???????? public:
?????????????????? Compute(intvalue1,int value2);?????????????????????????? //除法計算類構造函數,兩個整型參數列表
?????????????????? ~Compute(){}????????????????????????????????????????????????? //除法計算類空析構函數定義
?????????????????? void divideCompute();????????????????????????????????? //除法計算類除法計算方法成員
???????? private:
?????????????????? intm_value1;????????????????????????????????????????????????? //除法計算類數據成員,表示被除數
?????????????????? intm_value2;????????????????????????????????????????????????? //除法計算類數據成員,表示除數
};
//異常測試類,除法計算類源文件chapter1405_02.cpp
#include "chapter1405_02.h"
Compute::Compute(int value1,int value2)??????????????????????????? //除法計算類構造函數定義
{
???????? m_value1= value1;??????????????????????????????????????????????? //初始化被除數數據成員
???????? m_value2= value2;??????????????????????????????????????????????? //初始化除數數據成員
}
void Compute::divideCompute()?????????????????????????????????? //除法類除法計算方法成員定義
{
???????? try
???????? {
?????????????????? if(m_value2== 0)????????????????????????????????????????? //如果除數為0,則拋出相應異常
?????????????????? {
??????????????????????????? throwMyException("divisor is 0!");
?????????????????? }
???????? else??????????????????????????????????????????????????????????????????????????? //否則計算兩數相除,并打印結果
???????? {
?????????????????? cout<<"m_value1/m_value2:"<<(m_value1/m_value2)<<endl;
???????? }
???????? }
catch(MyException &e)???????????????????????????????????????? //捕獲對應異常類型
???????? {
?????????????????? string errorMessage = e.what();??????????????????????????? //定義錯誤信息字符串對象,獲取對應異常信息
?????????????????? cout<<errorMessage<<endl;?????????????????????? //打印輸出對應的異常信息
???????? }
catch(...)?????????????????????????????????????????????????????????????????? //捕獲所有異常
???????? {
?????????????????? string errorMessage = "Don’t knowerror!";//捕獲所有異常處理信息
?????????????????? cout<<errorMessage<<endl;
???????? }
}
int main()
{
???????? intvalue1,value2;??????????????????????????????????????????????????? //定義兩個整型變量
???????? cout<<"Pleaseinput two value:"<<endl;???????????? //提述輸入兩個整型數據
???????? cin>>value1>>value2;????????????????????????????????????????? //從鍵盤輸入兩個整型數
???????? Computecompute(value1,value2);???????????????????? //定義計算類對象,同時傳入輸入的兩個整型數構造初始化
???????? compute.divideCompute();?????????????????????????????????? //計算類對象調用其除法計算方法
???????? return0;
}
本實例通過封裝異常處理類類型,演示異常處理在C++應用程序中的使用方法。程序主要由異常類、程序計算類以及主函數組成,具體剖析見程序注釋與后面講解。
2.編輯makefile
Linux平臺下需要編譯源文件為chapter1405_02.cpp,相關makefile工程文件編譯命令編輯如下所示。
OBJECTS=chapter1405_02.o
CC=g++
chapter1405_02: $(OBJECTS)
???????? $(CC)$(OBJECTS) -g -o chapter1405_02
clean:
???????? rm-f chapter1405_02 core $(OBJECTS)
submit:
???????? cp-f -r chapter1405_02 ../bin
???????? cp-f -r *.h ../include
上述makefile文件套用前面的模板格式,主要替換了代碼文件、程序編譯中間文件、可執行程序等。在編譯命令部分-g選項的加入,表明程序編譯同時加入了可調式信息。
3.編譯運行程序
當前shell下執行make命令,生成可執行程序文件,隨后通過make submit命令提交程序文件至本實例bin目錄,通過cd命令定位至實例bin目錄,執行該程序文件運行結果如下所示。
[developer@localhost src]$ make
g++??? -c-o chapter1405_02.o chapter1405_02.cpp
g++ chapter1405_02.o -g -o chapter1405_02
[developer@localhost src]$ make submit
cp -f -r chapter1405_02 ../bin
cp -f -r *.h ../include
[developer@localhostsrc]$ cd ../bin
[developer@localhostbin]$ ./chapter1405_02
Please input two value:
12
0
divisor is 0!
上述實例中主要演示了異常機制在C++面向對象類類型中的應用情況。功能與前面除數為0的實例相仿。本實例中首先定義異常類MyException。該類主要用于用于異常拋出和捕捉時的對象類型。異常類中主要包含成員:四個不同類型的構函數、一個獲取異常信息的what方法成員、一個私有保護型的數據成員用于表示異常信息內容。
MyException類中3個構造函數,第一個為無參數默認構造定義,將異常信息內容初始化為空。第二個則參數為string型的對象構造,使用字符串初始化數據成員。第三個則參數為字符串指針的對象構造,使用字符串指針初始化異常數據成員。異常類中的方法成員what中主要返回異常信息,用于在應用程序中捕獲并打印輸出。
計算類主要用于兩數除法運算。該類中主要包含一個構造函數、一個空析構函數以及一個計算兩個數除法的方法。另外,它包含兩個整型的數據成員分別表示除法中的兩個數。該類的構造函數中主要通過傳入的兩個整型參數初始化對應的數據成員,計算除法的方法實現與前面的除法計算實例相同,僅僅是拋出的異常類型為MyException。另外捕捉異常catch塊中應用了異常類型對象實例調用其what方法獲取對應的異常信息并賦值最后輸出。
主程序中從鍵盤輸入兩個整型數,分別為12和0。定義Compute類對象實例,通過實參初始化計算類數據成員,隨后調用其除法計算方法。除法計算方法中由于除數為0,則拋出自定義異常類型同時傳入異常錯誤信息,隨后catch塊中捕獲到對應類型異常,內部則調用自定義異常類中what方法獲取異常出錯信息,最后打印輸出。
14.3.2??異常類的繼承處理
異常機制在繼承體系中依然可以很好的運用。最重要的是利用類繼承體系與對應的虛擬方法實現異常的多態性。通過自定義異常類派生新的異常類,除了包含基類基本異常處理方法,還可以包含自定義添加新的異常處理方式。下面將會在上述實例中添加異常派生類。新的異常派生類不僅僅可以在異常處拋出對應的錯誤信息,還可以支持錯誤代碼及對應的信息。
/**
*?實例chapter1406
*?源文件chapter1406_01.h
*?異常繼承類的封裝
*/
//異常處理類源文件chapter1406_01.h
#ifndef EXCEPTION_H_
#define EXCEPTION_H_
#include <iostream>
#include <string>
using namespace std;
//自定義異常類
class MyException
{
???????? public:
?????????????????? MyException()???????????????????????????????????????????????? //自定義異常類構造函數定義
?????????????????? {
??????????????????????????? m_context= "";????????????????????????????????????? //初始化類異常信息數據成員為空
?????????????????? }
?????????????????? virtual~MyException(){} ????????????????????????????? //異常類析構函數定義
?????????????????? MyException(const string & right)????????????? //異常類構造函數定義,通過傳入字符串型參數初始化
?????????????????? {
??????????????????????????? m_context= right;
?????????????????? }
?????????????????? MyException(const char * right)???????????????? //異常類構造函數定義,通過傳入字符串指針參數初始化
?????????????????? {
??????????????????????????? m_context= right;
?????????????????? }
?????????????????? virtualstring what() const ???????????????????????????????????? //異常類中虛擬what接口方法定義,獲取異常信息
?????????????????? {
??????????????????????????? returnm_context;
?????????????????? }?
???????? protected:
?????????????????? string?? m_context;?????? ??????????????????????? //異常類保護型數據成員,表示異常信息內容
};
//異常派生類,加入錯誤代碼處理
class code_MyException : public MyException
{
???????? public:
?????????????????? code_MyException(interror_code, string error_code_msg = "", string context ="")//異常派生類構造
?????????????????? {
??????????????????????????? m_errorCode= error_code;?????????????????????????????????????????????????????????????????????? //錯誤代碼初始化
??????????????????????????? m_errorCodeMsg= error_code_msg;???????????????????????????????????????????????????? //錯誤代碼對應消息初始化
??????????????????????????? m_context= context;???????????????????????????????????????????????????????????????????????????????????????????? //對應異常信息初始化
?????????????????? }
?????????????????? stringwhat() const ??????????????????????????????????????????????????????????????????????????????????????????????? //派生類what接口重定義
?????????????????? {
??????????????????????????? chartmp[20];????????????????????????????????????????????????????????????????????????????????????????????????? //定義臨時字符數組
??????????????????????????? sprintf(tmp,"%d", m_errorCode);????????????????????????????????????????????????????????????? //格式化錯誤代碼
??????????????????????????? returnstring(tmp) + "," + m_errorCodeMsg + "," + m_context;???????????? //返回連接的錯誤信息
?????????????????? }
???? ???????? intgetErrorCode() const ???????????????????????????????????????????????????????????????????????????????????? //獲取錯誤代碼
???? ???????? {
??????? ?? ???????? returnm_errorCode;
???? ???????? }
?????????????????? stringgetErrorCodeMsg() const ??????????????????????????????????????????????????????????????????????? //獲取錯誤代碼對應信息
?????????????????? {
??????????????????????????? returnm_errorCodeMsg;
?????????????????? }
???????? protected:
?????????????????? int? m_errorCode;???????????????????????????????????????????????????????????????????????????????????????????????? //錯誤代碼
?????????????????? string? m_errorCodeMsg;???????????????????????????????????????????????????????????????????????????????????????????? //錯誤信息
};
#endif
上述實例中在原來異常類的基礎之上派生了新的異常處理類,其中增加了錯誤代碼以及錯誤信息的功能。利用C++繼承機制,同時將其中的析構方法以及what接口定義為虛擬函數,有助于統一接口的實現。封裝實現的異常處理類可以將其編譯為庫,作為系統中統一的組件供程序來使用。該實例的實際應用以及測試由初學者按照前面實例的方式來實踐操作,從中領悟C++多態以及異常處理的好處。
14.4??標準庫提供異常處理類
C++標準庫同樣對異常作出了封裝實現。標準庫中異常實現也采用類繼承層次式的結構。庫中針對異常處理提供了一個層次化的類結構。該類層次中頂層基類為exception,即針對其它不同情況下的異常處理類都派生自exception類。
針對異常處理,總共提供了八個標準異常處理。其中,四個針對語言級提供的拋出異常,分別為bad_alloc、bad_cast、bad_typeid以及bad_exception;另外四個為C++標準庫拋出的異常,分別是out_of_range、invalid_argument、overflow_error以及ios_base::failure。下面將會針對這八類拋出異常情況作詳細的講述,分別說明每類異常表達的意義及基本用法。
1. bad_alloc
bad_alloc針對C++中動態存儲分配new的操作提供的異常信號,主要針對分配內存空間時內存耗盡(分配不足)時拋出的異常。這種異常的拋出有利于開發者在實際應用中針對new操作分配內存時提供某種分配出錯的處理方式。下面可以通過一個基本小代碼片段來理解bad_alloc異常處理情況。
void newChar()
{
???????? try
???????? {
?????????????????? newchar[10000000000000];???????????? //通過new開辟一個超大的內存空間
???????? }catch(bad_alloc)
???????? {
?????????????????? cout<<”memoryerror!”<<endl;
???????? }
}
上述代碼動態new了一個很大的內容空間,當本地計算機內存不足時會默認拋出bad_alloc異常。隨后catch捕捉到該異常后進入對應的異常處理塊中。
2.bad_cast
C++多態提供的dynamic_cast應用于指針類型時,通常需要考慮指針各類異常的情況。這時,可以顯式的去判斷處理。但是當dynamic_cast應用于引用時,即dynamic_cast<Type&>(object)表示引用的類型為Type,如果引用的不是對應類型的對象,則dynamic_cast將會默認隱式的作斷言檢查。這時就會拋出bad_cast異常,供catch捕獲檢測處理。
3. bad_typeid
bad_typeid是針對dynamic_cast操作空指針拋出的異常。
4. bad_exception
當應用程序出現了無法catch捕獲的異常情況時,通常會調用terminate終止程序。而使用bad_exception時,出現類似情況則會拋出bad_exception異常,用來替代終止程序。
bad_exception異常處理則主要針對應用提出的。這些類同樣從exception類派生而來。out_of_range表示操作數組等結構下標時,訪問超過設定范圍所拋出的異常。它主要針對容器中at()方法與重載的下標訪問運算符兩類方法。invalid_argument則表示向函數傳遞無效參數時拋出的異常,開發者可以針對該類拋出的異常作出相應的處理。overflow_error表示運算時候發生的上溢情況拋出的異常。ios_base::failure則是針對輸出輸入流操作時拋出的異常。
標準庫中異常處理的封裝可以通過查閱相關標準文檔,根據提供的相關處理接口在實際的應用程序中使用。實際的exception基類中除了基本的構造函數外,重載實現一些運算符操作。最重要的是其what接口的定義,該方法定義為虛擬方法供其派生類中改寫增加更多的異常處理。
14.5? C++應用程序錯誤處理
C++應用軟件系統開發中,通常情況下可以通過實現錯誤處理類來記錄程序啟動以及處理中可能出現的錯誤情況。錯誤處理采用自定義類類型來實現,除了起到異常處理的補充作用以外對于程序中必不可少的異常容錯處理來講,錯誤處理相對效率較高些。下面本小節主要通過自定義實現一個錯誤處理類的實例,詳細講述軟件應用程序中針對錯誤處理的另外一種錯誤信息提示方式。
1.準備實例
打開UE工具,創建新的空文件并另存為chapter1407_01.h、chapter1407_01.cpp、chapter1407_02.cpp。該代碼文件隨后會同makefile文件一起通過FTP工具傳輸至Linux服務器端,客戶端通過scrt工具訪問操作。程序代碼文件編輯如下所示。
//錯誤處理類頭文件chapter1407_01.h
#ifndef ERRORHANDLE_H_
#define ERRORHANDLE_H_
#include <iostream>
#include <string>
#include <deque>
using namespace std;
const int ERRORMSG_LEN = 300;??????????????????????????????????????????????????????????? //錯誤信息長度常量定義
class ErrorInfo?????????????????????????????????????????????????????????????????????????????????????????????? //錯誤信息類
{
???????? public:
?????????????????? ErrorInfo(){}???????????????????????????????????????????????????????????????????????????????? //錯誤信息空構造函數定義
?????????????????? ~ErrorInfo(){}?????????????????????????????????????????????????????????????????????????????? //錯誤信息空析構函數定義
?????????????????? ErrorInfo(constErrorInfo& errorinfo);????????????????????????????????????????????? //錯誤信息類拷貝構造
???????? public:
?????????????????? intm_errorNo;??????????????????????????????????????????????????????????????????????????? //錯誤信息類數據成員,表示錯誤代碼
?????????????????? charm_message[ERRORMSG_LEN+1];????????????????????????????? //錯誤信息類數據成員,表示錯誤信息
};
?
class ErrorMessages?????????????????????????????????????????????????????????????????????????????????? //錯誤消息容器類
{
???????? public:
?????????????????? voidsetProgramInfo(const string& programInfoName);??? //錯誤消息容器類設置應用程序信息方法
?????????????????? stringgetProgramInfo();?????????????????????????????????????????????????????????? //錯誤消息容器類獲取應用程序信息方法
?????????????????? voidinsert(ErrorInfo &errInfo);???????????????????????????????????????????????? //錯誤消息容器類插入錯誤信息方法
?????????????????? boolget(ErrorInfo& errInfo);???????????????????????????????????????????????????? //錯誤信息容器類獲取錯誤信息方法
?????????????????? voidclean();??????????????????????????????????????????????????????????????????????????????? //清空錯誤消息隊列方法
?????????????????? boolisEmpty();????????????????????????????????????? ???????????????????????????????????? //判斷錯誤消息隊列是否為空
?????????????????? voidinsert(const int errorCode, const string& errorMessage);?? //重載插入錯誤信息方法
?????????????????? voidinsert(const int errorCode, const char *errorMessage);????? //重載插入錯誤信息方法
???????? private:
?????????????????? stringm_programInfoName;?????????????????????????????????????????????????? //錯誤消息類數據成員,表示應用程序名
?????????????????? deque<ErrorInfo>m_errorInfoQueue;????????????????????????????????? //錯誤消息類數據成員,表述錯誤信息隊列
};
#endif
//錯誤處理類源文件chapter1407_01.cpp
#include "chapter1407_01.h"
?
ErrorInfo::ErrorInfo(const ErrorInfo&errorinfo)?????????????????????????????????????? //錯誤信息類拷貝構造定義
{
???????? m_errorNo= errorinfo.m_errorNo;????????????????????????????????????????????????? //拷貝傳入錯誤信息類對象的成員
???????? strcpy(m_message,errorinfo.m_message);
}
void ErrorMessages::setProgramInfo(conststring& programInfoName)?? //設置應用程序信息方法定義
{
???????? m_programInfoName= programInfoName;????????????????????????????????????????? //根據傳入的程序名設置
}
string ErrorMessages::getProgramInfo()???????????????????????????????????????????????? //獲取應用程序信息
{
???????? returnm_programInfoName;?????????????????????????????????????????????????????????? //返回應用程序名稱信息
}
void ErrorMessages::insert(ErrorInfo&errorinfo)?????????????????????????????????? //插入錯誤信息方法定義
{
???????? m_errorInfoQueue.push_back(errorinfo);?????????????????????????????????????????????? //將傳入的錯誤信息類對象放入錯誤信息隊列
}
void ErrorMessages::insert(const int errorCode,const string& errorMessage)//重載插入錯誤信息方法定義
{
???????? ErrorInfoerrorinfo;????????????????????????????????????????????????????????????????????????????? //定義錯誤信息類對象實例
???????? errorinfo.m_errorNo= errorCode;?????????????????????????????????????????????????? //將傳入的錯誤代碼賦值給該對象中數據成員
???????? if(errorMessage.length() <= ERRORMSG_LEN)????????????????????????? //如果傳入的錯誤信息長度在規定之內
?????????????????? strcpy(errorinfo.m_message,errorMessage.c_str());????????????????? //則將錯誤信息拷貝到該對象對應數據成員中
???????? else??????????????????????????????????????????????????????????????????????????????????????????????????????? //否則擴充長度后再拷貝
???????? {
?????????????????? strncpy(errorinfo.m_message,errorMessage.c_str(), ERRORMSG_LEN);
?????????????????? errorinfo.m_message[ERRORMSG_LEN]= 0;
???????? }
???????? insert(errorinfo);?????????????????????????????????????????????????????????????????????????????????? //最后插入到錯誤消息隊列中
}
void ErrorMessages::insert(const int errorCode,const char *errorMessage)//插入錯誤信息重載方法定義
{
???????? ErrorInfoerrorinfo; ??????????????????????????????????????????????????????????????????????????? //定義錯誤信息類對象
???????? errorinfo.m_errorNo= errorCode;?????????????????????????????????????????????????? //同上一方法定義
???????? if(strlen(errorMessage) <= ERRORMSG_LEN)
?????????????????? strcpy(errorinfo.m_message,errorMessage);
???????? else
???????? {
?????????????????? strncpy(errorinfo.m_message,errorMessage, ERRORMSG_LEN);
?????????????????? errorinfo.m_message[ERRORMSG_LEN]= 0;
???????? }
???????? insert(errorinfo);
}
bool ErrorMessages::get(ErrorInfo& errorinfo)?????????????????????????????????????? //獲取錯誤信息隊列中的信息
{
???????? if(m_errorInfoQueue.empty())???????????????????????????????????????????????????????? //如果錯誤信息隊列為空,則直接返回false
?????????????????? returnfalse;
???????? errorinfo= m_errorInfoQueue.front();????????????????????????????????????????????????????? //返回隊列中頭部的數據
???????? m_errorInfoQueue.pop_front();???????????????????????????????????????????????????????????????? //刪除隊列中頭部的數據
???????? returntrue;
}
void ErrorMessages::clean()????????????????????????????????????????????????????????????????????? //清空錯誤信息隊列
{
???????? m_errorInfoQueue.clear();????????????????????????????????????????????????????????????????????????? //直接調用clear方法清除
}
bool ErrorMessages::isEmpty()?????????????????????????????????????????????????????????????????????????? //判斷錯誤信息是否為空
{
???????? returnm_errorInfoQueue.empty();????????????????????????????????????????????????? //直接調用empty方法判斷
}
//錯誤處理類測試源文件chapter1407_02.cpp
#include "chapter1407_01.h"
?
ErrorMessages errorMessages;??????????????????????????????????????????????????????????????? //定義錯誤消息容器類對象
const int E_DIVISOR_ZERO = 200;?????????????????????????????????????????????????????????? //定義除數為0的錯誤代碼常量
const string E_DIVISOR_ERROR = "divisor is0!";????????????????????????????????? //定義除數為0的錯誤信息常量
void divideCompute(int value1,int value2)?????????????????????????????????????????????????????? //除法計算函數定義
{
???????? if(value2== 0)????????????????????????????????????????????????????????????????????????????????????? //如果除數為0,則記錄相應的錯誤信息
???????? {
?????????????????? errorMessages.insert(E_DIVISOR_ZERO,E_DIVISOR_ERROR);//插入對應的錯誤代碼與錯誤信息
???????? }
???????? else??????????????????????????????????????????????????????????????????????????????????????????????????????? //否則計算除法并輸出結果
???????? {
?????????????????? cout<<"value1/value2:"<<(value1/value2)<<endl;
???????? }
}
int main(int argc,char *argv[])
{
???????? stringname;??????????????????????????????????????????????????????????????????????????????? //定義字符串對象name
???????? ErrorInfoerrorInfo;???????????????????????????????????????????????????????????????????? //定義錯誤信息類
???????? name= argv[0];??????????????????????????????????????????????????????????????????????????????????? //獲取程序名并初始化字符串name
???????? errorMessages.setProgramInfo(name);??????????????????????????????? //調用錯誤信息容器類中設置程序信息方法
???????? cout<<"Programname:"<<errorMessages.getProgramInfo()<<endl;//打印輸出該程序信息
???????? intvalue1,value2;?????????????????????????????????????????????????????????????????????? //定義兩個整型變量
???????? cout<<"Pleaseinput two value:"<<endl;??????????????????????????????? //提示輸入兩個整型參數
???????? cin>>value1>>value2;????????????????????????????????????????????????????????????? //從鍵盤輸入兩個整型數
???????? divideCompute(value1,value2);?????????????????????????????????????????????????????? //調用計算除法運算的函數計算輸入的數據
???????? errorMessages.get(errorInfo);???????????????????????????????????????????????? //獲取錯誤消息隊列中的數據
???????? cout<<"errorcode:"<<errorInfo.m_errorNo<<" "
?????????????????? <<"errormessage:"<<errorInfo.m_message<<endl;????????? //打印輸出對應隊列中的錯誤信息
???????? return0;
}
本實例主要通過封裝程序錯誤處理類,演示應用程序中錯誤處理的一般實現方式。程序主要由錯誤處理類與主函數組成,程序具體剖析見程序注釋與后面的講解。
2.編輯makefile
Linux平臺下需要編譯源文件為chapter1407_01.cpp、chapter1407_02.cpp,相關makefile工程文件編譯命令編輯如下所示。
OBJECTS=chapter1407_01.o chapter1407_02.o
CC=g++
?
chapter1407_02: $(OBJECTS)
???????? $(CC)$(OBJECTS) -g -o chapter1407_02
chapter1407_01.o:chapter1407_01.h
chapter1407_02.o:
clean:
???????? rm-f chapter1407_02 core $(OBJECTS)
submit:
???????? cp-f -r chapter1407_02 ../bin
???????? cp-f -r *.h ../include
上述makefile文件套用前面的模板格式,主要替換了代碼文件、程序編譯中間文件、可執行程序等。在編譯命令部分-g選項的加入,表明程序編譯同時加入了可調式信息。
3.編譯運行程序
當前shell下執行make命令,生成可執行程序文件,隨后通過make submit命令提交程序文件至本實例bin目錄,通過cd命令定位至實例bin目錄,執行該程序文件運行結果如下所示。
[developer@localhost src]$ make
g++??? -c -ochapter1407_01.o chapter1407_01.cpp
g++??? -c -ochapter1407_02.o chapter1407_02.cpp
g++ chapter1407_01.o chapter1407_02.o -g -ochapter1407_02
[developer@localhost src]$ make submit
cp -f -r chapter1407_02 ../bin
cp -f -r *.h ../include
[developer@localhostsrc]$ cd ../bin
[developer@localhostbin]$ ./chapter1407_02
Program name:./chapter1407_02
Please input two value:
12
0
error code:200 error message:divisor is 0!
本實例中主要定義實現錯誤處理類。該類用于記錄軟件系統中自定義的錯誤代碼與對應的錯誤信息,以配合異常實現應用程序相應的錯誤處理。上述實例中錯誤處理主要涉及兩個類的定義:
q??一個為記錄錯誤信息類ErrorInfo。其中,主要包括相應的構造析構函數以及一個錯誤信息對象拷貝方法。它還包含兩個公開的數據成員,用于分別表示錯誤代碼與對應的錯誤信息。
q??另一個為錯誤信息容器類ErrorMessage。該類主要用于記錄并存放相應的錯誤處理信息,并提供相應的錯誤信息訪問接口,以供應用程序使用。
ErrorMessage類中主要包括八個接口方法與兩個數據成員。該類的接口方法在注釋中作了詳細的標注。其中,這些接口中最重要為幾個insert方法的重載實現,從而實現了不同種情況下的信息插入處理。第一個是整個結構體對象參數接口;第二個為支持錯誤代碼與字符串類型的對象接口;最后一個為支持錯誤代碼與字符串常量的插入信息接口。其中,還包括兩個保護數據成員,分別為應用程序名稱m_programInfoName和存放錯誤消息的隊列m_errorInfoQueue。該隊列中存放的為錯誤信息類類型的數據。
主程序中則仍然主要以測試除法運算中除數為0時的錯誤處理。首先從程序信息argv參數數組中獲取程序名稱設置并獲取打印該信息。隨后根據傳入的兩個整型數,調用對應的計算除法的運算方法。該方法中,一旦除數為0,則記錄對應的錯誤信息。最后主程序中則獲取到對應的錯誤信息并打印輸出。
上述程序中錯誤處理類可以放入自定義庫或者組件中,供應用程序中記錄錯誤信息使用。其中,支持錯誤代碼以及對應的錯誤信息對應記錄。這樣做的好處是針對不同的模塊設計可以規定一套錯誤代碼以及對應信息,供整個系統使用。另外,插入的錯誤信息可以根據實際需求,將其記錄到日志文件中,供出現錯誤時查詢使用。
14.6??小結
C++異常處理的提出為編寫出更具健壯性、穩定性的應用程序提供了很好的支持。但是異常與前面介紹該語言的特性一樣,不能夠濫用。因為任何支持的特性總是以犧牲一定的效率來實現的。通過上述介紹,用戶在實際軟件系統中可以結合錯誤處理、異常以及對應的錯誤日志等,最終開發出高穩定、高容錯性的應用系統。