欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++中try throw catch異常處理的用法示例

 更新時(shí)間:2019年10月20日 10:16:17   作者:麥葉旺  
這篇文章主要給大家介紹了關(guān)于C++中try throw catch異常處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

前言

今天在開發(fā)過程中調(diào)用一個(gè)庫函數(shù)結(jié)果庫函數(shù)有throw操作,當(dāng)前代碼沒有對throw進(jìn)行捕獲操作,導(dǎo)致進(jìn)程在main 函數(shù)中捕獲到異常導(dǎo)致進(jìn)程crash。所以借此記錄下c++關(guān)于try,throw,catch的用法。

程序運(yùn)行時(shí)常會(huì)碰到一些異常情況,例如:

  • 做除法的時(shí)候除數(shù)為 0;
  • 用戶輸入年齡時(shí)輸入了一個(gè)負(fù)數(shù);
  • 用 new 運(yùn)算符動(dòng)態(tài)分配空間時(shí),空間不夠?qū)е聼o法分配;
  • 訪問數(shù)組元素時(shí),下標(biāo)越界;打開文件讀取時(shí),文件不存在。

這些異常情況,如果不能發(fā)現(xiàn)并加以處理,很可能會(huì)導(dǎo)致程序崩潰。

所謂“處理”,可以是給出錯(cuò)誤提示信息,然后讓程序沿一條不會(huì)出錯(cuò)的路徑繼續(xù)執(zhí)行;也可能是不得不結(jié)束程序,但在結(jié)束前做一些必要的工作,如將內(nèi)存中的數(shù)據(jù)寫入文件、關(guān)閉打開的文件、釋放動(dòng)態(tài)分配的內(nèi)存空間等。

一發(fā)現(xiàn)異常情況就立即處理未必妥當(dāng),因?yàn)樵谝粋€(gè)函數(shù)執(zhí)行過程中發(fā)生的異常,在有的情況下由該函數(shù)的調(diào)用者決定如何處理更加合適。尤其像庫函數(shù)這類提供給程序員調(diào)用,用以完成與具體應(yīng)用無關(guān)的通用功能的函數(shù),執(zhí)行過程中貿(mào)然對異常進(jìn)行處理,未必符合調(diào)用它的程序的需要。

此外,將異常分散在各處進(jìn)行處理不利于代碼的維護(hù),尤其是對于在不同地方發(fā)生的同一種異常,都要編寫相同的處理代碼也是一種不必要的重復(fù)和冗余。如果能在發(fā)生各種異常時(shí)讓程序都執(zhí)行到同一個(gè)地方,這個(gè)地方能夠?qū)Ξ惓_M(jìn)行集中處理,則程序就會(huì)更容易編寫、維護(hù)。

鑒于上述原因,c++ 引入了異常處理機(jī)制。其基本思想是:函數(shù) A 在執(zhí)行過程中發(fā)現(xiàn)異常時(shí)可以不加處理,而只是“拋出一個(gè)異?!苯o A 的調(diào)用者,假定為函數(shù) B。

拋出異常而不加處理會(huì)導(dǎo)致函數(shù) A 立即中止,在這種情況下,函數(shù) B 可以選擇捕獲 A 拋出的異常進(jìn)行處理,也可以選擇置之不理。如果置之不理,這個(gè)異常就會(huì)被拋給 B 的調(diào)用者,以此類推。

如果一層層的函數(shù)都不處理異常,異常最終會(huì)被拋給最外層的 main 函數(shù)。main 函數(shù)應(yīng)該處理異常。如果main函數(shù)也不處理異常,那么程序就會(huì)立即異常地中止。

C++異常處理基本語法

C++ 通過 throw 語句和 try…catch 語句實(shí)現(xiàn)對異常的處理。throw 語句的語法如下:

throw  表達(dá)式;

該語句拋出一個(gè)異常。異常是一個(gè)表達(dá)式,其值的類型可以是基本類型,也可以是類。

try…catch 語句的語法如下:

try {
    語句組
}
catch(異常類型) {
    異常處理代碼
}
...
catch(異常類型) {
    異常處理代碼
}

catch 可以有多個(gè),但至少要有一個(gè)。

不妨把 try 和其后{}中的內(nèi)容稱作“try塊”,把 catch 和其后{}中的內(nèi)容稱作“catch塊”。

try…catch 語句的執(zhí)行過程是:

  • 執(zhí)行 try 塊中的語句,如果執(zhí)行的過程中沒有異常拋出,那么執(zhí)行完后就執(zhí)行最后一個(gè) catch 塊后面的語句,所有 catch 塊中的語句都不會(huì)被執(zhí)行;
  • 如果 try 塊執(zhí)行的過程中拋出了異常,那么拋出異常后立即跳轉(zhuǎn)到第一個(gè)“異常類型”和拋出的異常類型匹配的 catch 塊中執(zhí)行(稱作異常被該 catch 塊“捕獲”),執(zhí)行完后再跳轉(zhuǎn)到最后一個(gè) catch 塊后面繼續(xù)執(zhí)行。

例如下面的程序:

#include <iostream>
using namespace std;
int main()
{
 double m ,n;
 cin >> m >> n;
 try {
  cout << "before dividing." << endl;
  if( n == 0)
   throw -1; //拋出int類型異常
  else
   cout << m / n << endl;
  cout << "after dividing." << endl;
 }
 catch(double d) {
  cout << "catch(double) " << d << endl;
 }
 catch(int e) {
  cout << "catch(int) " << e << endl;
 }
 cout << "finished" << endl;
 return 0;
}

程序的運(yùn)行結(jié)果如下:

9 6↙
before dividing.
1.5
after dividing.
finished

說明當(dāng) n 不為 0 時(shí),try 塊中不會(huì)拋出異常。因此程序在 try 塊正常執(zhí)行完后,越過所有的 catch 塊繼續(xù)執(zhí)行,catch 塊一個(gè)也不會(huì)執(zhí)行。

程序的運(yùn)行結(jié)果也可能如下:

9 0↙
before dividing.
catch\(int) -1
finished

當(dāng) n 為 0 時(shí),try 塊中會(huì)拋出一個(gè)整型異常。拋出異常后,try 塊立即停止執(zhí)行。該整型異常會(huì)被類型匹配的第一個(gè) catch 塊捕獲,即進(jìn)入 catch(int e)  塊執(zhí)行,該 catch 塊執(zhí)行完畢后,程序繼續(xù)往后執(zhí)行,直到正常結(jié)束。

如果拋出的異常沒有被 catch 塊捕獲,例如,將catch(int e),改為catch(char e),當(dāng)輸入的 n 為 0 時(shí),拋出的整型異常就沒有 catch 塊能捕獲,這個(gè)異常也就得不到處理,那么程序就會(huì)立即中止,try…catch 后面的內(nèi)容都不會(huì)被執(zhí)行。

能夠捕獲任何異常的 catch 語句

如果希望不論拋出哪種類型的異常都能捕獲,可以編寫如下 catch 塊:

catch(...) {
 ...
}

這樣的 catch 塊能夠捕獲任何還沒有被捕獲的異常。例如下面的程序:

#include <iostream>
using namespace std;
int main()
{
  double m, n;
  cin >> m >> n;
  try {
    cout << "before dividing." << endl;
    if (n == 0)
      throw - 1; //拋出整型異常
    else if (m == 0)
      throw - 1.0; //拋出 double 型異常
    else
      cout << m / n << endl;
    cout << "after dividing." << endl;
  }
  catch (double d) {
    cout << "catch (double)" << d << endl;
  }
  catch (...) {
    cout << "catch (...)" << endl;
  }
  cout << "finished" << endl;
  return 0;
}

程序的運(yùn)行結(jié)果如下:

9 0↙
before dividing.
catch (...)
finished

當(dāng) n 為 0 時(shí),拋出的整型異常被catchy(...)捕獲。

程序的運(yùn)行結(jié)果也可能如下:

0 6↙
before dividing.
catch (double) -1
finished

當(dāng) m 為 0 時(shí),拋出一個(gè) double 類型的異常。雖然catch (double)和catch(...)都能匹配該異常,但是catch(double)是第一個(gè)能匹配的 catch 塊,因此會(huì)執(zhí)行它,而不會(huì)執(zhí)行catch(...)塊。

由于catch(...)能匹配任何類型的異常,它后面的 catch 塊實(shí)際上就不起作用,因此不要將它寫在其他 catch 塊前面。

異常的再拋出

如果一個(gè)函數(shù)在執(zhí)行過程中拋出的異常在本函數(shù)內(nèi)就被 catch 塊捕獲并處理,那么該異常就不會(huì)拋給這個(gè)函數(shù)的調(diào)用者(也稱為“上一層的函數(shù)”);如果異常在本函數(shù)中沒有被處理,則它就會(huì)被拋給上一層的函數(shù)。

例如下面的程序:

#include <iostream>
#include <string>
using namespace std;
class CException
{
public:
  string msg;
  CException(string s) : msg(s) {}
};
double Devide(double x, double y)
{
  if (y == 0)
    throw CException("devided by zero");
  cout << "in Devide" << endl;
  return x / y;
}
int CountTax(int salary)
{
  try {
    if (salary < 0)
      throw - 1;
    cout << "counting tax" << endl;
  }
  catch (int) {
    cout << "salary < 0" << endl;
  }
  cout << "tax counted" << endl;
  return salary * 0.15;
}
int main()
{
  double f = 1.2;
  try {
    CountTax(-1);
    f = Devide(3, 0);
    cout << "end of try block" << endl;
  }
  catch (CException e) {
    cout << e.msg << endl;
  }
  cout << "f = " << f << endl;
  cout << "finished" << endl;
  return 0;
}

程序的輸出結(jié)果如下:

salary < 0
tax counted
devided by zero
f=1.2
finished

CountTa 函數(shù)拋出異常后自行處理,這個(gè)異常就不會(huì)繼續(xù)被拋給調(diào)用者,即 main 函數(shù)。因此在 main 函數(shù)的 try 塊中,CountTax 之后的語句還能正常執(zhí)行,即會(huì)執(zhí)行f = Devide(3, 0);。

第 35 行,Devide 函數(shù)拋出了異常卻不處理,該異常就會(huì)被拋給 Devide 函數(shù)的調(diào)用者,即 main 函數(shù)。拋出此異常后,Devide 函數(shù)立即結(jié)束,第 14 行不會(huì)被執(zhí)行,函數(shù)也不會(huì)返回一個(gè)值,這從第 35 行 f 的值不會(huì)被修改可以看出。

Devide 函數(shù)中拋出的異常被 main 函數(shù)中類型匹配的 catch 塊捕獲。第 38 行中的 e 對象是用復(fù)制構(gòu)造函數(shù)初始化的。

如果拋出的異常是派生類的對象,而 catch 塊的異常類型是基類,那么這兩者也能夠匹配,因?yàn)榕缮悓ο笠彩腔悓ο蟆?/p>

雖然函數(shù)也可以通過返回值或者傳引用的參數(shù)通知調(diào)用者發(fā)生了異常,但采用這種方式的話,每次調(diào)用函數(shù)時(shí)都要判斷是否發(fā)生了異常,這在函數(shù)被多處調(diào)用時(shí)比較麻煩。有了異常處理機(jī)制,可以將多處函數(shù)調(diào)用都寫在一個(gè) try 塊中,任何一處調(diào)用發(fā)生異常都會(huì)被匹配的 catch 塊捕獲并處理,也就不需要每次調(diào)用后都判斷是否發(fā)生了異常。

有時(shí),雖然在函數(shù)中對異常進(jìn)行了處理,但是還是希望能夠通知調(diào)用者,以便讓調(diào)用者知道發(fā)生了異常,從而可以作進(jìn)一步的處理。在 catch 塊中拋出異常可以滿足這種需要。例如:

#include <iostream>
#include <string>
using namespace std;
int CountTax(int salary)
{
  try {
    if( salary < 0 )
      throw string("zero salary");
    cout << "counting tax" << endl;

  }
  catch (string s ) {
    cout << "CountTax error : " << s << endl;
    throw; //繼續(xù)拋出捕獲的異常
  }
  cout << "tax counted" << endl;
  return salary * 0.15;
}
int main()
{
  double f = 1.2;
  try {
    CountTax(-1);
    cout << "end of try block" << endl;
  }
  catch(string s) {
    cout << s << endl;
  }
  cout << "finished" << endl;
  return 0;
}

程序的輸出結(jié)果如下:

CountTax error:zero salary
zero salary
finished

第 14 行的throw;沒有指明拋出什么樣的異常,因此拋出的就是 catch 塊捕獲到的異常,即 string("zero salary")。這個(gè)異常會(huì)被 main 函數(shù)中的 catch 塊捕獲。

函數(shù)的異常聲明列表

為了增強(qiáng)程序的可讀性和可維護(hù)性,使程序員在使用一個(gè)函數(shù)時(shí)就能看出這個(gè)函數(shù)可能會(huì)拋出哪些異常,C++ 允許在函數(shù)聲明和定義時(shí),加上它所能拋出的異常的列表,具體寫法如下:

void func() throw (int, double, A, B, C);

void func() throw (int, double, A, B, C){...}

上面的寫法表明 func 可能拋出 int 型、double 型以及 A、B、C 三種類型的異常。異常聲明列表可以在函數(shù)聲明時(shí)寫,也可以在函數(shù)定義時(shí)寫。如果兩處都寫,則兩處應(yīng)一致。

如果異常聲明列表如下編寫:

void func() throw ();

則說明 func 函數(shù)不會(huì)拋出任何異常。

一個(gè)函數(shù)如果不交待能拋出哪些類型的異常,就可以拋出任何類型的異常。

函數(shù)如果拋出了其異常聲明列表中沒有的異常,在編譯時(shí)不會(huì)引發(fā)錯(cuò)誤,但在運(yùn)行時(shí), Dev C++ 編譯出來的程序會(huì)出錯(cuò);用 Visual Studio 2010 編譯出來的程序則不會(huì)出錯(cuò),異常聲明列表不起實(shí)際作用。

C++標(biāo)準(zhǔn)異常類

C++ 標(biāo)準(zhǔn)庫中有一些類代表異常,這些類都是從 exception 類派生而來的。常用的幾個(gè)異常類如圖 1 所示。

                                                                                           

                                                                                                     圖1:常用的異常類

 

bad_typeid、bad_cast、bad_alloc、ios_base::failure、out_of_range 都是 exception 類的派生類。C++ 程序在碰到某些異常時(shí),即使程序中沒有寫 throw 語句,也會(huì)自動(dòng)拋出上述異常類的對象。這些異常類還都有名為 what 的成員函數(shù),返回字符串形式的異常描述信息。使用這些異常類需要包含頭文件 stdexcept。

下面分別介紹以上幾個(gè)異常類。本節(jié)程序的輸出以 Visual Studio 2010為準(zhǔn),Dev C++ 編譯的程序輸出有所不同。

1) bad_typeid

使用 typeid 運(yùn)算符時(shí),如果其操作數(shù)是一個(gè)多態(tài)類的指針,而該指針的值為 NULL,則會(huì)拋出此異常。

2) bad_cast

在用 dynamic_cast 進(jìn)行從多態(tài)基類對象(或引用)到派生類的引用的強(qiáng)制類型轉(zhuǎn)換時(shí),如果轉(zhuǎn)換是不安全的,則會(huì)拋出此異常。程序示例如下:

#include <iostream>
#include <stdexcept>
using namespace std;
class Base
{
  virtual void func() {}
};
class Derived : public Base
{
public:
  void Print() {}
};
void PrintObj(Base & b)
{
  try {
    Derived & rd = dynamic_cast <Derived &>(b);
    //此轉(zhuǎn)換若不安全,會(huì)拋出 bad_cast 異常
    rd.Print();
  }
  catch (bad_cast & e) {
    cerr << e.what() << endl;
  }
}
int main()
{
  Base b;
  PrintObj(b);
  return 0;
}

程序的輸出結(jié)果如下:

Bad dynamic_cast!

在 PrintObj 函數(shù)中,通過 dynamic_cast 檢測 b 是否引用的是一個(gè) Derived 對象,如果是,就調(diào)用其 Print 成員函數(shù);如果不是,就拋出異常,不會(huì)調(diào)用 Derived::Print。

3) bad_alloc

在用 new 運(yùn)算符進(jìn)行動(dòng)態(tài)內(nèi)存分配時(shí),如果沒有足夠的內(nèi)存,則會(huì)引發(fā)此異常。程序示例如下:

#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
  try {
    char * p = new char[0x7fffffff]; //無法分配這么多空間,會(huì)拋出異常
  }
  catch (bad_alloc & e) {
    cerr << e.what() << endl;
  }
  return 0;
}

程序的輸出結(jié)果如下:

bad allocation
ios_base::failure

在默認(rèn)狀態(tài)下,輸入輸出流對象不會(huì)拋出此異常。如果用流對象的 exceptions 成員函數(shù)設(shè)置了一些標(biāo)志位,則在出現(xiàn)打開文件出錯(cuò)、讀到輸入流的文件尾等情況時(shí)會(huì)拋出此異常。此處不再贅述。

4) out_of_range

用 vector 或 string 的 at 成員函數(shù)根據(jù)下標(biāo)訪問元素時(shí),如果下標(biāo)越界,則會(huì)拋出此異常。例如:

#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
using namespace std;
int main()
{
  vector<int> v(10);
  try {
    v.at(100) = 100; //拋出 out_of_range 異常
  }
  catch (out_of_range & e) {
    cerr << e.what() << endl;
  }
  string s = "hello";
  try {
    char c = s.at(100); //拋出 out_of_range 異常
  }
  catch (out_of_range & e) {
    cerr << e.what() << endl;
  }
  return 0;
}

程序的輸出結(jié)果如下:

invalid vector <T> subscript
invalid string position

如果將v.at(100)換成v[100],將s.at(100)換成s[100],程序就不會(huì)引發(fā)異常(但可能導(dǎo)致程序崩潰)。因?yàn)?at 成員函數(shù)會(huì)檢測下標(biāo)越界并拋出異常,而 operator[] 則不會(huì)。operator [] 相比 at 的好處就是不用判斷下標(biāo)是否越界,因此執(zhí)行速度更快。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • C/C++ 開發(fā)神器CLion使用入門超詳細(xì)教程

    C/C++ 開發(fā)神器CLion使用入門超詳細(xì)教程

    這篇文章主要介紹了C/C++ 開發(fā)神器CLion使用入門超詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • C/C++中for語句循環(huán)用法以及練習(xí)舉例

    C/C++中for語句循環(huán)用法以及練習(xí)舉例

    for語句是一種循環(huán)語句,它是對while語句的推廣,下面這篇文章主要給大家介紹了關(guān)于C/C++中for語句循環(huán)用法以及練習(xí)舉例的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • OpenCV實(shí)現(xiàn)最小外接正矩形

    OpenCV實(shí)現(xiàn)最小外接正矩形

    這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)最小外接正矩形,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C語言實(shí)現(xiàn)火車訂票系統(tǒng)

    C語言實(shí)現(xiàn)火車訂票系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)火車訂票系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Qt的qDebug使用小結(jié)

    Qt的qDebug使用小結(jié)

    使用qDebug()函數(shù)它可以把調(diào)試信息直接輸出到控制臺(tái)上,本文就來介紹一下qDebug的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-06-06
  • C語言詳細(xì)分析不同類型數(shù)據(jù)在內(nèi)存中的存儲(chǔ)

    C語言詳細(xì)分析不同類型數(shù)據(jù)在內(nèi)存中的存儲(chǔ)

    使用編程語言進(jìn)行編程時(shí),需要用到各種變量來存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類型,來分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么
    2022-08-08
  • C++實(shí)現(xiàn)病人就醫(yī)管理系統(tǒng)

    C++實(shí)現(xiàn)病人就醫(yī)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++語言實(shí)現(xiàn)病人就醫(yī)管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • EasyC++內(nèi)部鏈接性和無鏈接性

    EasyC++內(nèi)部鏈接性和無鏈接性

    這篇文章主要介紹了EasyC++內(nèi)部鏈接性和無鏈接性,當(dāng)我們使用static關(guān)鍵字,將變量的作用于限制在整個(gè)文件時(shí),該變量的鏈接性為內(nèi)部鏈接性,然而無鏈接性的變量其實(shí)就是在代碼塊當(dāng)中使用static關(guān)鍵字創(chuàng)建的,接下來一起進(jìn)入文章了解更多內(nèi)容吧
    2021-12-12
  • C++利用GPAC實(shí)現(xiàn)生成MP4文件的示例代碼

    C++利用GPAC實(shí)現(xiàn)生成MP4文件的示例代碼

    GPAC主要針對學(xué)生和內(nèi)容創(chuàng)作者,代表了一個(gè)跨平臺(tái)的多媒體框架,開發(fā)人員可以使用它在?LGPL?許可下制作開源媒體。本文就來用GPAC實(shí)現(xiàn)生成MP4文件,感興趣的可以了解一下
    2023-02-02
  • C++中std::count函數(shù)介紹和使用場景

    C++中std::count函數(shù)介紹和使用場景

    std::count函數(shù)是一個(gè)非常實(shí)用的算法,它可以幫助我們快速統(tǒng)計(jì)給定值在指定范圍內(nèi)的出現(xiàn)次數(shù),本文主要介紹了C++中std::count函數(shù)介紹和使用場景,感興趣的可以了解一下
    2024-02-02

最新評論