38、不一樣的C++系列--C++的異常處理
阿新 • • 發佈:2019-01-03
C++的異常處理
異常處理介紹
C++內建了異常處理的語法元素 try … catch …
- try語句處理正常程式碼邏輯
- catch語句處理異常情況
- try語句中的異常由對應的catch語句處理
- 語法:
try
{
double r = divide(1, 0);
}
catch(...)
{
cout << "Divided by zero..." << endl;
}
- C++通過throw語句丟擲異常資訊
double divide(double a, double b)
{
const double delta = 0.0000000000001 ;
double ret = 0;
if(!((-delta < b) && (b < delta)))
{
ret = a / b;
}
else
{
//產生除0異常
throw 0;
}
return ret;
}
異常處理分析
- 這裡對C++異常處理分析一下:
- throw丟擲的異常必須被catch處理
- 當前函式能夠處理異常,程式繼續往下執行
- 當前函式無法處理異常,則函式停止執行,並返回
- throw丟擲的異常必須被catch處理
未被處理的異常會順著函式呼叫棧向上傳播,知道被處理為止,否則程式將停止執行。
function1 ==> function2 ==> function3
<== <== throw1;
這裡舉一個例子:
#include <iostream>
#include <string>
using namespace std;
double divide(double a, double b)
{
const double delta = 0.000000000000001;
double ret = 0;
if( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
//用throw丟擲異常資訊
throw 0;
}
return ret;
}
int main(int argc, char *argv[])
{
try
{
double r = divide(1, 0);
cout << "r = " << r << endl;
}
catch(...)
{
//try語句中丟擲的異常在這裡接受並處理
cout << "Divided by zero..." << endl;
}
return 0;
}
繼續學習 try… catch …的知識點:
同一個try 語句可以跟上多個catch語句
- catch語句可以定義具體處理的異常型別
- 不同型別的異常由不同的catch語句負責處理
- try語句中可以丟擲任何型別的異常
- catch( … ) 用於處理所有型別的異常
- 任何異常都只能被捕獲(catch)一次
異常處理的匹配規則:
//異常處理匹配時,不進行任何的型別轉換
| try
| {
| throw 1;
| }
| catch(Type1 t1)
| {
| }
| catch(Type2 t2)
| {
| }
| catch(TypeN tn)
| {
| }
v
異常丟擲後,自上而下嚴格匹配每一個catch語句處理的型別
這裡用一個例子來試驗一下匹配規則:
#include <iostream>
#include <string>
using namespace std;
void Demo1()
{
try
{
//這裡丟擲一個字元
throw 'c';
}
catch(char c)
{
cout << "catch(char c)" << endl;
}
catch(short c)
{
cout << "catch(short c)" << endl;
}
catch(double c)
{
cout << "catch(double c)" << endl;
}
catch(...)
{
cout << "catch(...)" << endl;
}
}
void Demo2()
{
//這裡丟擲string類字串
throw string("D.T.Software");
}
int main(int argc, char *argv[])
{
Demo1();
try
{
Demo2();
}
catch(char* s)
{
cout << "catch(char *s)" << endl;
}
catch(const char* cs)
{
cout << "catch(const char *cs)" << endl;
}
catch(string ss)
{
cout << "catch(string ss)" << endl;
}
return 0;
}
執行結果如下:
catch(char c)
catch(string ss)
catch再丟擲異常
在try … catch … 語句中,catch語句塊中可以丟擲異常
try
{
func();
}
catch(int i)
{
//將捕獲的異常重新丟擲
throw i;
}
catch(...)
{
//將捕獲的異常重新丟擲
throw;
}
//catch中丟擲的異常需要外層的try ... catch ...捕獲
可是為什麼要重新丟擲異常呢?因為:
catch中捕獲的異常可以被重新解釋後丟擲,這樣就可以在工程開發中使用這樣的方式統一異常型別。
//工程開發
通過呼叫 MyFunc 獲得 func函式的功能和統一的異常資訊
|
| 呼叫
V
//私有庫
void MyFunc(int i);
/*異常型別為Exception*/
|
| 封裝
V
//第三方庫
void func(int i);
/*異常型別為int*/
這裡舉一個例子:
#include <iostream>
#include <string>
using namespace std;
void Demo()
{
try
{
try
{
//這裡丟擲的異常由內層try ... catch ...來捕獲處理
throw 'c';
}
catch(int i)
{
cout << "Inner: catch(int i)" << endl;
//這裡再次丟擲的異常由外層來捕獲並處理
throw i;
}
catch(...)
{
cout << "Inner: catch(...)" << endl;
//這裡再次丟擲的異常由外層來捕獲並處理
throw;
}
}
catch(...)
{
cout << "Outer: catch(...)" << endl;
}
}
/*
假設: 當前的函式式第三方庫中的函式,因此,我們無法修改原始碼
函式名: void func(int i)
丟擲異常的型別: int
-1 ==》 引數異常
-2 ==》 執行異常
-3 ==》 超時異常
*/
void func(int i)
{
if( i < 0 )
{
throw -1;
}
if( i > 100 )
{
throw -2;
}
if( i == 11 )
{
throw -3;
}
cout << "Run func..." << endl;
}
void MyFunc(int i)
{
try
{
func(i);
}
catch(int i)
{
switch(i)
{
case -1:
throw "Invalid Parameter";
break;
case -2:
throw "Runtime Exception";
break;
case -3:
throw "Timeout Exception";
break;
}
}
}
int main(int argc, char *argv[])
{
Demo();
try
{
MyFunc(11);
}
catch(const char* cs)
{
cout << "Exception Info: " << cs << endl;
}
return 0;
}
執行結果為:
Inner: catch(...)
Outer: catch(...)
Exception Info: Timeout Exception
自定義異常型別
自定義類型別及匹配:
- 異常的型別可以是自定義類型別
- 對於類型別異常的匹配依舊是自上而下嚴格匹配
- 賦值相容性原則在異常匹配中依然適用
- 一般而言
- 匹配子類異常的catch放在上部
- 匹配父類異常的catch放在下部
工程中的異常類:
- 在工程中會定義一系列的異常類
- 每個類代表工程中可能出現的一種異常型別
- 程式碼複用時可能需要重解釋不同的異常類
- 在定義 catch語句塊時推薦使用引用作為引數
這裡舉一個例子:
#include <iostream>
#include <string>
using namespace std;
class Base
{
};
class Exception : public Base
{
int m_id;
string m_desc;
public:
Exception(int id, string desc)
{
m_id = id;
m_desc = desc;
}
int id() const
{
return m_id;
}
string description() const
{
return m_desc;
}
};
/*
假設: 當前的函式式第三方庫中的函式,因此,我們無法修改原始碼
函式名: void func(int i)
丟擲異常的型別: int
-1 ==》 引數異常
-2 ==》 執行異常
-3 ==》 超時異常
*/
void func(int i)
{
if( i < 0 )
{
throw -1;
}
if( i > 100 )
{
throw -2;
}
if( i == 11 )
{
throw -3;
}
cout << "Run func..." << endl;
}
void MyFunc(int i)
{
try
{
func(i);
}
catch(int i)
{
switch(i)
{
case -1:
//這裡直接丟擲一個類
throw Exception(-1, "Invalid Parameter");
break;
case -2:
//這裡直接丟擲一個類
throw Exception(-2, "Runtime Exception");
break;
case -3:
//這裡直接丟擲一個類
throw Exception(-3, "Timeout Exception");
break;
}
}
}
int main(int argc, char *argv[])
{
try
{
MyFunc(11);
}
//接受到的時候 判斷為引用型別
catch(const Exception& e)
{
cout << "Exception Info: " << endl;
cout << " ID: " << e.id() << endl;
cout << " Description: " << e.description() << endl;
}
//接受到的時候 判斷為引用型別
catch(const Base& e)
{
cout << "catch(const Base& e)" << endl;
}
return 0;
}
執行結果為:
Exception Info:
ID: -3
Description: Timeout Exception
C++標準庫的異常類族
在C++標準庫中提供了實用異常類族
- 標準庫中的異常都是從 exception 類派生的
- exception 類有兩個主要的分支
- logic_error
- 常用於程式中的可避免邏輯錯誤
- runtime_error
- 常用於程式中無法避免的惡性錯誤
- logic_error
這裡演示一下如何使用:
#include <iostream>
#include <string>
#include "Array.h"
#include "HeapArray.h"
using namespace std;
void TestArray()
{
Array<int, 5> a;
/*
這裡如果越界會丟擲異常:
throw out_of_range("T& Array<T, N>::operator[] (int index)");
throw out_of_range("T Array<T, N>::operator[] (int index) const");
*/
for(int i=0; i<a.length(); i++)
{
a[i] = i;
}
for(int i=0; i<a.length(); i++)
{
cout << a[i] << endl;
}
}
void TestHeapArray()
{
HeapArray<double>* pa = HeapArray<double>::NewInstance(5);
if( pa != NULL )
{
HeapArray<double>& array = pa->self();
/*
這裡如果越界會丟擲異常:
throw out_of_range("T& HeapArray<T>::operator [] (int index)");
throw out_of_range("T HeapArray<T>::operator [] (int index) const");
*/
for(int i=0; i<array.length(); i++)
{
array[i] = i;
}
for(int i=0; i<array.length(); i++)
{
cout << array[i] << endl;
}
}
delete pa;
}
int main(int argc, char *argv[])
{
try
{
TestArray();
cout << endl;
TestHeapArray();
}
catch(...)
{
cout << "Exception" << endl;
}
return 0;
}
小結
- C++中直接支援異常處理的概念
- try … catch …是C++中異常處理的專用語句
- try 語句處理正常程式碼邏輯,catch 語句處理異常情況
- 同一個 try 語句可以跟上多個 catch 語句
- 異常處理必須嚴格匹配,不進行任何的型別轉換
- catch語句塊中可以丟擲異常
- 異常的型別可以是自定義類型別
- 賦值相容性原則在異常匹配中依然適用
- 標準庫中的異常都是從exception類派生的