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

C++ 程序拋出異常后執(zhí)行順序說明

 更新時(shí)間:2021年02月24日 14:27:08   作者:無鞋童鞋  
這篇文章主要介紹了C++ 程序拋出異常后執(zhí)行順序說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧

1 析構(gòu)函數(shù)中是否可以拋出異常

首先我們看一個(gè)常見的問題,析構(gòu)函數(shù)中是否可以拋出異常。答案是C++標(biāo)準(zhǔn)指明析構(gòu)函數(shù)不能、也不應(yīng)該拋出異常!

C++異常處理模型是為C++語言量身設(shè)計(jì)的,更進(jìn)一步的說,它實(shí)際上也是為C++語言中面向?qū)ο蠖?wù)的。

C++異常處理模型最大的特點(diǎn)和優(yōu)勢就是對C++中的面向?qū)ο筇峁┝俗顝?qiáng)大的無縫支持。

那么如果對象在運(yùn)行期間出現(xiàn)了異常,C++異常處理模型有責(zé)任清除那些由于出現(xiàn)異常所導(dǎo)致的已經(jīng)失效了的對象(也即對象超出了它原來的作用域),并釋放對象原來所分配的資源, 這就是調(diào)用這些對象的析構(gòu)函數(shù)來完成釋放資源的任務(wù),所以從這個(gè)意義上說,析構(gòu)函數(shù)已經(jīng)變成了異常處理的一部分。

下面我們來看看析構(gòu)函數(shù)中不能拋出異常的兩個(gè)理由:

1)如果析構(gòu)函數(shù)拋出異常,則異常點(diǎn)之后的程序不會執(zhí)行,如果析構(gòu)函數(shù)在異常點(diǎn)之后執(zhí)行了某些必要的動(dòng)作比如釋放某些資源,則這些動(dòng)作不會執(zhí)行,會造成諸如資源泄漏的問題。

2)通常異常發(fā)生時(shí),c++的機(jī)制會調(diào)用已經(jīng)構(gòu)造對象的析構(gòu)函數(shù)來釋放資源,此時(shí)若析構(gòu)函數(shù)本身也拋出異常,則前一個(gè)異常尚未處理,又有新的異常,會造成程序崩潰的問題。

那么當(dāng)無法保證在析構(gòu)函數(shù)中不發(fā)生異常時(shí), 該怎么辦?

其實(shí)還是有很好辦法來解決的。那就是把異常完全封裝在析構(gòu)函數(shù)內(nèi)部,決不讓異常拋出函數(shù)之外。這是一種非常簡單,也非常有效的方法。

//析構(gòu)函數(shù)
~Class()
{
 try{
 }
 catch(){ //這里可以什么都不做,只是保證catch塊的程序拋出的異常不會被扔出析構(gòu)函數(shù)之外。
 }
}

2 程序拋出異常后會怎樣

下面我們通過一個(gè)程序來觀察當(dāng)程序中拋出異常了是否會調(diào)用析構(gòu)函數(shù),異常拋出中throw()后面的語句是否還會執(zhí)行。

程序如下,我們創(chuàng)建一個(gè)類,然后構(gòu)造一個(gè)類對象,當(dāng)拋出異常我們看程序是否會進(jìn)入析構(gòu)函數(shù)以及throw()拋出異常后面的程序:

#include<iostream>
using namespace std;
class setTry{
public:
 setTry(){ //構(gòu)造函數(shù)
  cout << "start!" << endl; // 1
 }
 ~setTry(){ //析構(gòu)函數(shù)
  cout << "end!" << endl; // 4
 }
 void dosomething(){
  cout << "do something!" << endl; //類方法
 }
};
int main(void)
{
 setTry newOne;
 try{
  throw("error!"); //直接拋出異常 
  newOne.dosomething();
 }
 catch (char* one){ //接收char*類異常 
  cout << one << endl; // 2
 }
 catch (...){   //接收其他類型異常
  cout << "..." << endl;
 }
 cout << "return 0!"<<endl; // 3
 return 0;
}

上面程序運(yùn)行結(jié)就是按標(biāo)注的1、2、3、4步驟輸出的,結(jié)果如下圖所示:

從運(yùn)行結(jié)果就可以看出,拋出異常try內(nèi)部的throw()后面程序不會再執(zhí)行,而try外部后面的程序會繼續(xù)執(zhí)行。另外,析構(gòu)函數(shù)在生存期結(jié)束也會被調(diào)用。

補(bǔ)充:C++異常捕獲和處理

0. 寫在前面

異常,讓一個(gè)函數(shù)可以在發(fā)現(xiàn)自己無法處理的錯(cuò)誤時(shí)拋出一個(gè)異常,希望它的調(diào)用者可以直接或者間接處理這個(gè)問題。而傳統(tǒng)錯(cuò)誤處理技術(shù),檢查到一個(gè)錯(cuò)誤,返回退出碼或者終止程序等等,此時(shí)我們只知道有錯(cuò)誤,但不能更清楚的知道哪種錯(cuò)誤,因此,使用異常,就把錯(cuò)誤和處理分開來,由庫函數(shù)拋出異常,由調(diào)用者捕獲這個(gè)異常,調(diào)用者就可以知道程序函數(shù)庫調(diào)用出現(xiàn)錯(cuò)誤了,并去處理,而是否終止程序就把握在調(diào)用者手里了。

1. 異常的拋出和處理

1. 異常處理的語句

try區(qū)段:這個(gè)區(qū)段中包含了可能發(fā)生異常的代碼,在發(fā)生了異常之后,需要通過throw拋出。

throw子句:throw 子句用于拋出異常,被拋出的異常可以是C++的內(nèi)置類型(例如: throw int(1);),也可以是自定義類型。

catch子句:每個(gè)catch子句都代表著一種異常的處理。catch子句用于處理特定類型的異常。

例2:

 #include <iostream>
 using namespace std;
 void Test1()
 {
  try
  {
   char* p = new char[0x7fffffff]; //拋出異常
  }
  catch (exception e)
  {
   cout << e.what() << endl; //捕獲異常,然后程序結(jié)束
  }
 }
 int main()
 {
  Test1();
  system("pause");
  return 0;
 }

結(jié)果:

當(dāng)使用new進(jìn)行開空間時(shí),申請內(nèi)存失敗,就會拋出異常,此時(shí)捕獲到異常時(shí),就可告訴使用者是哪里的錯(cuò)誤,便于修改

2. 異常的處理規(guī)則

異常是通過拋出對象而引發(fā)的,該對象的類型決定了應(yīng)該激活哪個(gè)處理代碼。

被選中的處理代碼是調(diào)用鏈中與該對象類型匹配且離拋出異常位置最近的那一個(gè)。

拋出異常后會釋放局部存儲對象,所以被拋出的對象也就還給系統(tǒng)了,throw表達(dá)式會初始化一個(gè)拋出特殊的匿名對象,異常對象由編譯管理,異常對象在傳給對應(yīng)的catch處理之后撤銷。

例2:

class Exception//異常類
{
public:
 Exception(const string& msg, int id)
 {
  _msg = msg;
  _id = id;
 }
 const char* What() const
 {
  return _msg.c_str();
 }
protected:
 string _msg;
 int _id;
};
template<size_t N = 10>
class Array
{
public:
 int& operator[](size_t pos)
 {
  if (pos >= N)
  {
   Exception e("下標(biāo)不合法", 1); //出了這個(gè)作用域,拋出的異常對象就銷毀了,這時(shí)會生成一個(gè)匿名對象先接受這個(gè)對象,并傳到外層棧幀。
   throw e;
  }
  return a[pos];
 }
protected:
 int a[N];
};
int f()
{
 try
 {
  Array<> a;
  a[11];
 }
 catch (exception& e)
 {
  cout << e.what() << endl; //類型不匹配,找離拋出異常位置最近且類型匹配的那個(gè)。
 }
 return 0;
}
int main()
{
 try
 {
  f();
 }
 catch (Exception& e)
 {
  cout << e.What() << endl;
 }
 system("pause");
 return 0;
} 

結(jié)果:

f()函數(shù)中捕獲的異常是標(biāo)準(zhǔn)庫里面的異常,但拋出異常的對象是自己定義的異常類,故類型不匹配,找離拋出異常最近的且類型匹配的Exception

3. 異常處理?xiàng)U归_

1.在try的語句塊內(nèi)聲明的變量在外部是不可以訪問的,即使是在catch子句內(nèi)也不可以訪問?! ?/p>

2.棧展開(尋找異常處理(exception handling)代碼)

棧展開會沿著嵌套函數(shù)的調(diào)用鏈不斷查找,知道找到了已拋出的異常匹配的catch子句。如果在最后還是沒有找到對應(yīng)的catch子句的話,則退出主函數(shù)后查找過程終止,程序調(diào)用標(biāo)準(zhǔn)函數(shù)庫的terminate()函數(shù),終止該程序的執(zhí)行

具體過程:

當(dāng)一個(gè)exception被拋出的時(shí)候,控制權(quán)會從函數(shù)調(diào)用中釋放出來,并需找一個(gè)可以處理的catch子句

對于一個(gè)拋出異常的try區(qū)段,程序會先檢查與該try區(qū)段關(guān)聯(lián)的catch子句,如果找到了匹配的catch子句,就使用這個(gè)catch子句處理這個(gè)異常。

沒有找到匹配的catch子句,如果這個(gè)try區(qū)段嵌套在其他try區(qū)段中,則繼續(xù)檢查與外層try匹配的catch子句。如果仍然沒有找到匹配的catch子句,則退出當(dāng)前這個(gè)主調(diào)函數(shù),并在調(diào)用了剛剛退出的這個(gè)函數(shù)的其他函數(shù)中尋找。

3. catch子句的查找:  

catch子句是按照出現(xiàn)的順序進(jìn)行匹配的(以例2來說,異常先會匹配catch(exception e)子句,然后在匹配 catch (Exception e)子句,一步一步的棧展開)。在尋找catch子句的過程中,拋出的異常可以進(jìn)行類型轉(zhuǎn)換,但是比較嚴(yán)格:

允許從非常量轉(zhuǎn)換到常量的類型轉(zhuǎn)換(權(quán)限縮?。?/p>

允許從派生類到基類的轉(zhuǎn)換。

允許數(shù)組被轉(zhuǎn)換成為指向數(shù)組(元素)類型的指針,函數(shù)被轉(zhuǎn)換成指向該函數(shù)類型的指針(降級問題)

標(biāo)準(zhǔn)算術(shù)類型的轉(zhuǎn)換(比如:把bool型和char型轉(zhuǎn)換成int型)和類類型轉(zhuǎn)換(使用類的類型轉(zhuǎn)換運(yùn)算符和轉(zhuǎn)換構(gòu)造函數(shù))。

4. 異常處理中需要注意的問題

如果拋出的異常一直沒有函數(shù)捕獲(catch),則會一直上傳到c++運(yùn)行系統(tǒng)那里,導(dǎo)致整個(gè)程序的終止

一般在異常拋出后資源可以正常被釋放,但注意如果在類的構(gòu)造函數(shù)中拋出異常,系統(tǒng)是不會調(diào)用它的析構(gòu)函數(shù)的,處理方法是:如果在構(gòu)造函數(shù)中要拋出異常,則在拋出前要記得刪除申請的資源。

異常處理僅僅通過類型而不是通過值來匹配的,所以catch塊的參數(shù)可以沒有參數(shù)名稱,只需要參數(shù)類型。

函數(shù)原型中的異常說明要與實(shí)現(xiàn)中的異常說明一致,否則容易引起異常沖突。

應(yīng)該在throw語句后寫上異常對象時(shí),throw先通過Copy構(gòu)造函數(shù)構(gòu)造一個(gè)新對象,再把該新對象傳遞給 catch.  

注:那么當(dāng)異常拋出后新對象如何釋放?

異常處理機(jī)制保證:異常拋出的新對象并非創(chuàng)建在函數(shù)棧上,而是創(chuàng)建在專用的異常棧上,因此它才可以跨接多個(gè)函數(shù)而傳遞到上層,否則在棧清空的過程中就會被銷毀。所有從try到throw語句之間構(gòu)造起來的對象的析構(gòu)函數(shù)將被自動(dòng)調(diào)用。但如果一直上溯到main函數(shù)后還沒有找到匹配的catch塊,那么系統(tǒng)調(diào)用terminate()終止整個(gè)程序,這種情況下不能保證所有局部對象會被正確地銷毀。

catch塊的參數(shù)推薦采用地址傳遞而不是值傳遞,不僅可以提高效率,還可以利用對象的多態(tài)性。另外,派生類的異常撲獲要放到父類異常撲獲的前面,否則,派生類的異常無法被撲獲。

編寫異常說明時(shí),要確保派生類成員函數(shù)的異常說明和基類成員函數(shù)的異常說明一致,即派生類改寫的虛函數(shù)的異常說明至少要和對應(yīng)的基類虛函數(shù)的異常說明相同,甚至更加嚴(yán)格,更特殊。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • C語言中雙向鏈表和雙向循環(huán)鏈表詳解

    C語言中雙向鏈表和雙向循環(huán)鏈表詳解

    這篇文章主要介紹了C語言中雙向鏈表和雙向循環(huán)鏈表詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C/C++函數(shù)調(diào)用的幾種方式總結(jié)

    C/C++函數(shù)調(diào)用的幾種方式總結(jié)

    本篇文章主要是對C/C++函數(shù)調(diào)用的幾種方式進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2013-12-12
  • C++使用鏈表存儲實(shí)現(xiàn)通訊錄功能管理

    C++使用鏈表存儲實(shí)現(xiàn)通訊錄功能管理

    這篇文章主要為大家詳細(xì)介紹了C++使用鏈表存儲實(shí)現(xiàn)通訊錄功能管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C++對象排序的比較你了解嗎

    C++對象排序的比較你了解嗎

    這篇文章主要為大家詳細(xì)介紹了C++對象排序的比較,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • VS2022創(chuàng)建Windows服務(wù)程序的方法步驟

    VS2022創(chuàng)建Windows服務(wù)程序的方法步驟

    本文主要介紹了VS2022創(chuàng)建Windows服務(wù)程序的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • C語言編程數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解小白篇

    C語言編程數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解小白篇

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ),非常適合初學(xué)數(shù)據(jù)結(jié)構(gòu)的小白,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-09-09
  • C++模板特例化應(yīng)用實(shí)例

    C++模板特例化應(yīng)用實(shí)例

    這篇文章主要介紹了C++模板特例化應(yīng)用實(shí)例,是非常重要的一個(gè)概念,需要的朋友可以參考下
    2014-08-08
  • 深入理解Java事務(wù)的原理與應(yīng)用

    深入理解Java事務(wù)的原理與應(yīng)用

    下面小編就為大家?guī)硪黄钊肜斫釰ava事務(wù)的原理與應(yīng)用。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • C語言實(shí)現(xiàn)楊輝三角實(shí)例

    C語言實(shí)現(xiàn)楊輝三角實(shí)例

    這篇文章主要介紹了C語言實(shí)現(xiàn)楊輝三角的方法,主要通過數(shù)組簡單實(shí)現(xiàn),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-09-09
  • 探討++i與i++哪個(gè)效率更高

    探討++i與i++哪個(gè)效率更高

    i++總是要?jiǎng)?chuàng)建一個(gè)臨時(shí)對象,在退出函數(shù)時(shí)還要銷毀它,而且返回臨時(shí)對象的值時(shí)還會調(diào)用其拷貝構(gòu)造函數(shù)
    2013-10-10

最新評論