assert函數:
C語言和C++都有一個專為調試而准備的工具函數,就是 assert()函數。 這個函數是在C語言的 assert.h 庫文件里定義的,所以包含到C++程序里我們用以下語句:
#include <cassert>
assert()函數需要有一個參數,它將測試這個輸入參數的真 or 假狀態。 如果為真,Do nothing,繼續往下執行! 如果為假,中斷執行!
看下邊演示:test.cpp
#include <cassert> int main() { int i = 20; assert( i == 65 ); return 0; }
結果:
test.cpp, line 6 This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. 請按任意鍵繼續. . .
演示中,我們看到 assert()函數可以幫助我們調試程序。 我們可以利用它在某個程序里的關鍵假設不成立時立刻停止該程序的執行並報錯,從而避免發生更嚴重的問題。 另外,除了結合 assert()函數,在程序的開發、測試階段,我們還可以使用大量的 cout 語句來報告在程序里正在發生的事情。
用戶體驗與程序猿體驗:
對運行時錯誤的處理分為兩種情況:
- 一種與程序猿有關,在開發、測試和調試程序的過程中,程序猿需要盡可能詳細的信息來查找和糾正各種潛在的運行時錯誤。
- 另一種情況與最終用戶有關,在使用一個程序的過程中,錯誤處理應該把用戶的感受擺在第一位!
在理想的情況下,程序發布之前,它里邊的所有錯誤都應該被發現和改正過來。只可惜這是幾乎不可能的,就連微軟這樣的大公司也做不出這樣的保證!最為一條原則:最終用戶看到的錯誤信息應該既專業又清晰,不能輕易中斷程序,不能充滿技術細節。
捕獲異常:
同樣為了對付潛在的編程錯誤(尤其是運行時的錯誤),捕獲異常是一種完全不同的辦法。 簡單地說,異常(exception)就是與預期不相符合的反常現象。 基本使用思路:
- 安排一些C++代碼(try語句)去嘗試某件事 —— 尤其是那些可能會失敗的事(比如打開一個文件或申請一些內存)
- 如果發生問題,就拋出一個異常(throw語句)
- 再安排一些代碼(catch語句)去捕獲這個異常並進行相應的處理。
捕獲異常的基本語法如下:
try { // Do something. // Throw an exception on error. } catch { // Do whatever. }
Pay attention!每條 try 語句至少要有一條配對的 catch 語句。必須定義 catch 語句以便讓它接收一個特定類型的參數。
C++還允許我們定義多條 catch 語句,讓每條 catch 語句分別對應着一種可能的異常類型:
catch(int e){ … } catch(bool e){ … } catch(…){ … }
最后一條 catch 語句可以捕獲任何類型的異常。
在程序里,我們可以用 throw 保留字來拋出一個異常:throw 1; 在某個 try 語句塊里執行過 throw 語句,它后面的所有語句(截止到這個 try 語句塊末尾)將永遠不會被執行。 與使用一個條件語句或 return 語句相比,采用異常處理機制的好處是它可以把程序的正常功能和邏輯與出錯處理部分清晰地划分開而不是讓它們混雜在一起。
如何讓函數拋出異常:
你可以在定義一個函數時明確地表明你想讓它拋出一個異常,為了表明你想讓它拋出哪種類型的異常,可以使用如下所示語法:
type functionName(arguments) throw(type);
如果沒有使用這種語法來定義函數,就意味着函數可以拋出任意類型的異常。 注:有些編譯器不支持這種語法,則可省略 throw(type) 部分。
#include <iostream> #include <climits> unsigned long returnFactorial(unsigned short num) throw (const char *); //在 returnFactorial函數內可能會拋出字符串類型的異常 int main() { unsigned short num = 0; std::cout << "請輸入一個整數: "; while( !(std::cin>>num) || (num<1) ) { std::cin.clear(); // 清除狀態 std::cin.ignore(100, '\n'); // 清除緩沖區 std::cout << "請輸入一個整數:"; } std::cin.ignore(100, '\n'); try { unsigned long factorial = returnFactorial(num); std::cout << num << "的階乘值是: " << factorial; } catch(const char *e) { std::cout << e; } return 0; } unsigned long returnFactorial(unsigned short num) throw (const char *) { unsigned long sum = 1; unsigned long max = ULONG_MAX; for( int i=1; i <= num; i++ ) { sum *= i; max /= i; } if( max < 1 ) { throw "悲催。。。該基數太大,無法在該計算機計算求出階乘值。\n"; } else { return sum; } }
TIPS:
- 如何使用異常是一個很容易引起爭論的話題。有些程序員使用異常來處理幾乎所有的錯誤,但C++的創始人Bjarne Stroustrup(BS君)覺得它們正在被濫用。 所以使用異常的基本原則是:應該只用它們來處理確實可能不正常的情況。
- 作為一條原則,在構造器和析構器里不應該使用異常。一位非常有經驗的程序猿在這些方法里成功地使用了異常是有可能的,但稍有不慎就會導致嚴重的問題。
- 如果 try 語句塊無法找到一個與之匹配的 catch 語句塊,它拋出的異常將中止程序的執行。
- 在C++標准庫里有個名為 exception 的文件,該文件聲明了一個 exception 的基類。可以用這個基類來創建個人的子類以管理異常。 有經驗的程序猿常常這么做,而如此拋出和捕獲的是 exception 類或其子類的對象。
- 如果你打算使用對象作為異常,請記住這樣一個原則:以”值傳遞”方式拋出對象,以”引用傳遞”方式捕獲對象。