天天看點

【C++深度解析】46、C++ 中的異常處理(上)

文章目錄

    • 1 異常的概念
    • 2 C++中的異常
    • 3 try…catch…詳解
      • 3.1 一個 try 跟多個 catch
      • 3.2 try…catch 用于分割
      • 3.3 指定可能抛出的異常類型
    • 4 小結

程式在運作過程中可能産生異常,異常與 Bug 的差別在于,異常是程式運作時可預料的執行分支,Bug 是程式中的錯誤,是不被預期的運作方式。下面列舉幾個常見的異常與 Bug。

異常

  • 運作時産生除 0 的情況
  • 需要打開的外部檔案不存在
  • 數組通路時越界

Bug

  • 使用野指針
  • 堆數組使用後未釋放
  • 選擇排序無法處理長度為 0 的數組

C++ 内置了異常處理的文法元素 try…catch…

  • try 語句處理正常代碼邏輯,catch 語句處理異常情況
  • try 語句中的異常由對應的 catch 語句處理

C++ 通過 throw 語句抛出異常資訊

舉例如下:

【C++深度解析】46、C++ 中的異常處理(上)

divide 中 throw 抛出的異常會被 try…catch… 捕獲

throw 抛出的異常必須被 catch 處理,目前函數能夠處理異常,程式繼續往下執行,目前函數函數無法處理異常,則函數停止執行,并傳回。未被處理的異常會順着函數調用棧向上傳播,直到被處理為止,否則程式将被停止執行。

【C++深度解析】46、C++ 中的異常處理(上)

程式設計實驗:C++異常處理初探

// 46-1.cpp
#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 0;
    }
    return ret;
}
int main(int argc, char *argv[])
{
    try
    {
        double r = divide(1, 0);
        cout << "r = " << r << endl;
    }
    catch(...)
    {
        cout << "Divide by zero..." << endl;
    }
    return 0;
}
           

程式首先執行 try 内中的代碼,divide 函數中的 throw 抛出異常,該函數中沒有 catch 捕獲異常,傳回到 main 函數中,被 catch 捕獲。

編譯運作:

$ g++ 46-1.cpp -o 46-1
$ ./46-1
Divide by zero...
           

同一個 try 語句可以跟上多個 catch 語句。

  • catch 語句可以定義為具體處理的異常類型,不同類型的異常由不同的 catch 語句負責處理
  • try 語句中可以抛出任何類型的異常
  • catch(…) 用于處理所有類型的異常
  • 任何類型的異常隻能被捕獲一次
【C++深度解析】46、C++ 中的異常處理(上)

catch(…) 必須要放在最後,自上而下要嚴格比對,包括 const 屬性。

程式設計實驗:異常類型比對

// 46-2.cpp
#include<iostream>
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()
{
    throw "error";
}
int main()
{
    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;
}
           

函數 Demo1 中的異常在函數中被 catch 捕獲,有四個 catch,分别捕獲 char,short,double,和其他所有類型。

Demo2 中的異常傳回到 main 函數中才被捕獲,異常處理必須嚴格比對,包括 const 屬性,抛出的異常 “error” 被 catch(const char* cs) 捕獲。要被 catch(string ss) 捕獲,需要 throw string(“error”);

$ g++ 46-2.cpp -o  46-2
$ ./46-2
catch(char c)
catch(const char* cs)
           

  • try…catch 用于分割正常代碼與異常代碼

下面的代碼寫法是正确的

【C++深度解析】46、C++ 中的異常處理(上)

  • 為了提高代碼可讀性,在函數聲明和定義時可以直接指定可能抛出的異常類型

下面的代碼指出函數可能抛出的異常類型為 int

// 46-1.cpp
#include<iostream>
using namespace std;

int func(int i, int j) throw(int, char)		// 指定可以抛出int和char異常
{
    if (0 < j && j < 10)
    {
        return i + j;
    }
    else
    {
        throw '0';
    }
}

void test(int i) try				// try…catch 用于分割正常代碼與異常代碼
{
    cout << "func(i, i) = " << func(i, i) << endl;
}
catch (int i)
{
    cout << "Exception: " << i << endl;
}
catch (...)
{
    cout << "Exception..." << endl;
}

int main(int argc, char* argv[])
{
    test(4);
    test(10);
    return 0;
}
           
func(i, i) = 8
Exception...
           

繼續閱讀