免费观看又色又爽又黄的小说免费_美女福利视频国产片_亚洲欧美精品_美国一级大黄大色毛片

怎么在C++中實(shí)現(xiàn)異常機(jī)制-創(chuàng)新互聯(lián)

怎么在C++中實(shí)現(xiàn)異常機(jī)制?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括信陽(yáng)網(wǎng)站建設(shè)、信陽(yáng)網(wǎng)站制作、信陽(yáng)網(wǎng)頁(yè)制作以及信陽(yáng)網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,信陽(yáng)網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到信陽(yáng)省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

1、C函數(shù)的調(diào)用和返回

要理解C++異常機(jī)制實(shí)現(xiàn)之前,首先要了解一個(gè)函數(shù)的調(diào)用和返回機(jī)制,這里面就要涉及到ESP和EBP寄存器。我們先看一下函數(shù)調(diào)用和返回的流程。

下面是按調(diào)用約定__stdcall 調(diào)用函數(shù)test(int p1,int p2)的匯編代碼
假設(shè)執(zhí)行函數(shù)前堆棧指針ESP為NN
push p2 ;參數(shù)2入棧, ESP -= 4h , ESP = NN - 4h
push p1 ;參數(shù)1入棧, ESP -= 4h , ESP = NN - 8h
call test ;壓入返回地址 ESP -= 4h, ESP = NN - 0Ch
{
push ebp ;保護(hù)先前EBP指針, EBP入棧, ESP-=4h, ESP = NN - 10h
mov ebp, esp ;設(shè)置EBP指針指向棧頂 NN-10h
mov eax, dword ptr [ebp+0ch] ;ebp+0ch為NN-4h,即參數(shù)2的位置
mov ebx, dword ptr [ebp+08h] ;ebp+08h為NN-8h,即參數(shù)1的位置
sub esp, 8 ;局部變量所占空間ESP-=8, ESP = NN-18h
...
add esp, 8 ;釋放局部變量, ESP+=8, ESP = NN-10h
pop ebp ;出棧,恢復(fù)EBP, ESP+=4, ESP = NN-0Ch
ret 8 ;ret返回,彈出返回地址,ESP+=4, ESP=NN-08h, 后面加操作數(shù)8為平衡堆棧,ESP+=8,ESP=NN, 恢復(fù)進(jìn)入函數(shù)前的堆棧.
}

函數(shù)棧架構(gòu)主要承載著以下幾個(gè)部分:

1、傳遞參數(shù):通常,函數(shù)的調(diào)用參數(shù)總是在這個(gè)函數(shù)棧框架的最頂端。

2、傳遞返回地址:告訴被調(diào)用者的 return 語(yǔ)句應(yīng)該 return 到哪里去,通常指向該函數(shù)調(diào)用的下一條語(yǔ)句(代碼段中的偏移)。

3、存放調(diào)用者的當(dāng)前棧指針:便于清理被調(diào)用者的所有局部變量、并恢復(fù)調(diào)用者的現(xiàn)場(chǎng)。

4、存放當(dāng)前函數(shù)內(nèi)的所有局部變量:記得嗎?剛才說(shuō)過(guò)所有局部和臨時(shí)變量都是存儲(chǔ)在棧上的。  

怎么在C++中實(shí)現(xiàn)異常機(jī)制

2、C++函數(shù)調(diào)用

首先澄清一點(diǎn),這里說(shuō)的 “C++ 函數(shù)”是指:

1、該函數(shù)可能會(huì)直接或間接地拋出一個(gè)異常:即該函數(shù)的定義存放在一個(gè) C++ 編譯(而不是傳統(tǒng) C)單元內(nèi),并且該函數(shù)沒(méi)有使用“throw()”異常過(guò)濾器

2、該函數(shù)的定義內(nèi)使用了 try 塊。

以上兩者滿足其一即可。為了能夠成功地捕獲異常和正確地完成棧回退(stack unwind),編譯器必須要引入一些額外的數(shù)據(jù)結(jié)構(gòu)和相應(yīng)的處理機(jī)制。我們首先來(lái)看看引入了異常處理機(jī)制的棧框架大概是什么樣子:

怎么在C++中實(shí)現(xiàn)異常機(jī)制

由圖2可見(jiàn),在每個(gè) C++ 函數(shù)的棧框架中都多了一些東西。仔細(xì)觀察的話,你會(huì)發(fā)現(xiàn),多出來(lái)的東西正好是一個(gè) EXP 類型的結(jié)構(gòu)體。進(jìn)一步分析就會(huì)發(fā)現(xiàn),這是一個(gè)典型的單向鏈表式結(jié)構(gòu):

piPrev 成員指向鏈表的上一個(gè)節(jié)點(diǎn),它主要用于在函數(shù)調(diào)用棧中逐級(jí)向上尋找匹配的 catch 塊,并完成棧回退工作。

piHandler 成員指向完成異常捕獲和棧回退所必須的數(shù)據(jù)結(jié)構(gòu)(主要是兩張記載著關(guān)鍵數(shù)據(jù)的表:“try”塊表:tblTryBlocks 及“棧回退表”:tblUnwind)。

nStep 成員用來(lái)定位 try 塊,以及在棧回退表中尋找正確的入口。

需要說(shuō)明的是:編譯器會(huì)為每一個(gè)“C++ 函數(shù)”定義一個(gè) EHDL 結(jié)構(gòu),不過(guò)只會(huì)為包含了“try”塊的函數(shù)定義 tblTryBlocks 成員。此外,異常處理器還會(huì)為每個(gè)線程維護(hù)一個(gè)指向當(dāng)前異常處理框架的指針。該指針指向異常處理器鏈表的鏈尾,通常存放在某個(gè) TLS 槽或能起到類似作用的地方。

3、棧回退(stack unwind)

“棧回退”是伴隨異常處理機(jī)制引入 C++ 中的一個(gè)新概念,主要用來(lái)確保在異常被拋出、捕獲并處理后,所有生命期已結(jié)束的對(duì)象都會(huì)被正確地析構(gòu),它們所占用的空間會(huì)被正確地回收。下面我們就來(lái)具體看看編譯器是如何實(shí)現(xiàn)棧回退機(jī)制的:

怎么在C++中實(shí)現(xiàn)異常機(jī)制

圖中的“FuncUnWind”函數(shù)內(nèi),所有真實(shí)代碼均以黑色和藍(lán)色字體標(biāo)示,編譯器生成的代碼則由灰色和橙色字體標(biāo)明。此時(shí),在圖里給出的 nStep 變量和 tblUnwind 成員作用就十分明顯了。

nStep 變量用于跟蹤函數(shù)內(nèi)局部對(duì)象的構(gòu)造、析構(gòu)階段。再配合編譯器為每個(gè)函數(shù)生成的 tblUnwind 表,就可以完成退棧機(jī)制。表中的 pfnDestroyer 字段記錄了對(duì)應(yīng)階段應(yīng)當(dāng)執(zhí)行的析構(gòu)操作(析構(gòu)函數(shù)指針);pObj 字段則記錄了與之相對(duì)應(yīng)的對(duì)象 this 指針偏移。將 pObj 所指的偏移值加上當(dāng)前棧框架基址(EBP),就是要代入 pfnDestroyer 所指析構(gòu)函數(shù)的 this 指針,這樣即可完成對(duì)該對(duì)象的析構(gòu)工作。而 nNextIdx 字段則指向下一個(gè)需要析構(gòu)對(duì)象所在的行(下標(biāo))。

在發(fā)生異常時(shí),異常處理器首先檢查當(dāng)前函數(shù)棧框架內(nèi)的 nStep 值,并通過(guò) piHandler 取得 tblUnwind[] 表。然后將 nStep 作為下標(biāo)帶入表中,執(zhí)行該行定義的析構(gòu)操作,然后轉(zhuǎn)向由 nNextIdx 指向的下一行,直到 nNextIdx 為 -1 為止。在當(dāng)前函數(shù)的棧回退工作結(jié)束后,異常處理器可沿當(dāng)前函數(shù)棧框架內(nèi) piPrev 的值回溯到異常處理鏈中的上一節(jié)點(diǎn)重復(fù)上述操作,直到所有回退工作完成為止。

值得一提的是,nStep 的值完全在編譯時(shí)決定,運(yùn)行時(shí)僅需執(zhí)行若干次簡(jiǎn)單的整形立即數(shù)賦值(通常是直接賦值給CPU里的某個(gè)寄存器)。此外,對(duì)于所有內(nèi)部類型以及使用了默認(rèn)構(gòu)造、析構(gòu)方法(并且它的所有成員和基類也使用了默認(rèn)方法)的類型,其創(chuàng)建和銷毀均不影響 nStep 的值。

注意:如果在棧回退的過(guò)程中,由于析構(gòu)函數(shù)的調(diào)用而再次引發(fā)了異常(異常中的異常),則被認(rèn)為是一次異常處理機(jī)制的嚴(yán)重失敗。此時(shí)進(jìn)程將被強(qiáng)行禁止。為防止出現(xiàn)這種情況,應(yīng)在所有可能拋出異常的析構(gòu)函數(shù)中使用“std::uncaught_exception()”方法判斷當(dāng)前是否正在進(jìn)行棧回退(即:存在一個(gè)未捕獲或未完全處理完畢的異常)。如是,則應(yīng)抑制異常的再次拋出。

4、異常捕獲

一個(gè)異常被拋出時(shí),就會(huì)立即引發(fā) C++ 的異常捕獲機(jī)制:

怎么在C++中實(shí)現(xiàn)異常機(jī)制

在上一小節(jié)中,我們已經(jīng)看到了 nStep 變量在跟蹤對(duì)象構(gòu)造、析構(gòu)方面的作用。實(shí)際上 nStep 除了能夠跟蹤對(duì)象創(chuàng)建、銷毀階段以外,還能夠標(biāo)識(shí)當(dāng)前執(zhí)行點(diǎn)是否在 try 塊中,以及(如果當(dāng)前函數(shù)有多個(gè) try 塊的話)究竟在哪個(gè) try 塊中。這是通過(guò)在每一個(gè) try 塊的入口和出口各為 nStep 賦予一個(gè)唯一 ID 值,并確保 nStep 在對(duì)應(yīng) try 塊內(nèi)的變化恰在此范圍之內(nèi)來(lái)實(shí)現(xiàn)的。

在具體實(shí)現(xiàn)異常捕獲時(shí),首先,C++ 異常處理器檢查發(fā)生異常的位置是否在當(dāng)前函數(shù)的某個(gè) try 塊之內(nèi)。這項(xiàng)工作可以通過(guò)將當(dāng)前函數(shù)的 nStep 值依次在 piHandler 指向tblTryBlocks[] 表的條目中進(jìn)行范圍為 [nBeginStep, nEndStep) 的比對(duì)來(lái)完成。

例如:若圖4 中的 FuncB 在 nStep == 2 時(shí)發(fā)生了異常,則通過(guò)比對(duì) FuncB 的 tblTryBlocks[] 表發(fā)現(xiàn) 2∈[1, 3),故該異常發(fā)生在 FuncB 內(nèi)的第一個(gè) try 塊中。

其次,如果異常發(fā)生的位置在當(dāng)前函數(shù)中的某個(gè) try 塊內(nèi),則嘗試匹配該 tblTryBlocks[] 相應(yīng)條目中的 tblCatchBlocks[] 表。tblCatchBlocks[] 表中記錄了與指定 try 塊配套出現(xiàn)的所有 catch 塊相關(guān)信息,包括這個(gè) catch 塊所能捕獲的異常類型及其起始地址等信息。

若找到了一個(gè)匹配的 catch 塊,則復(fù)制當(dāng)前異常對(duì)象到此 catch 塊,然后跳轉(zhuǎn)到其入口地址執(zhí)行塊內(nèi)代碼。

否則,則說(shuō)明異常發(fā)生位置不在當(dāng)前函數(shù)的 try 塊內(nèi),或者這個(gè) try 塊中沒(méi)有與當(dāng)前異常相匹配的 catch 塊,此時(shí)則沿著函數(shù)棧框架中 piPrev 所指地址(即:異常處理鏈中的上一個(gè)節(jié)點(diǎn))逐級(jí)重復(fù)以上過(guò)程,直至找到一個(gè)匹配的 catch 塊或到達(dá)異常處理鏈的首節(jié)點(diǎn)。對(duì)于后者,我們稱為發(fā)生了未捕獲的異常,對(duì)于 C++ 異常處理器而言,未捕獲的異常是一個(gè)嚴(yán)重錯(cuò)誤,將導(dǎo)致當(dāng)前進(jìn)程被強(qiáng)制結(jié)束。

5、拋出異常

接下來(lái)討論整個(gè) C++ 異常處理機(jī)制中的最后一個(gè)環(huán)節(jié),異常的拋出:

怎么在C++中實(shí)現(xiàn)異常機(jī)制

在編譯一段 C++ 代碼時(shí),編譯器會(huì)將所有 throw 語(yǔ)句替換為其 C++ 運(yùn)行時(shí)庫(kù)中的某一指定函數(shù),這里我們叫它 __CxxRTThrowExp(與本文提到的所有其它數(shù)據(jù)結(jié)構(gòu)和屬性名一樣,在實(shí)際應(yīng)用中它可以是任意名稱)。該函數(shù)接收一個(gè)編譯器認(rèn)可的內(nèi)部結(jié)構(gòu)(我們叫它 EXCEPTION 結(jié)構(gòu))。這個(gè)結(jié)構(gòu)中包含了待拋出異常對(duì)象的起始地址、用于銷毀它的析構(gòu)函數(shù),以及它的 type_info 信息。對(duì)于沒(méi)有啟用 RTTI 機(jī)制(編譯器禁用了 RTTI 機(jī)制或沒(méi)有在類層次結(jié)構(gòu)中使用虛表)的異常類層次結(jié)構(gòu),可能還要包含其所有基類的 type_info 信息,以便與相應(yīng)的 catch 塊進(jìn)行匹配。

在圖中的深灰色框圖內(nèi),我們使用 C++ 偽代碼展示了函數(shù) FuncA 中的 “throw myExp(1);” 語(yǔ)句將被編譯器最終翻譯成的樣子。實(shí)際上在多數(shù)情況下,__CxxRTThrowExp 函數(shù)即我們前面曾多次提到的“異常處理器”,異常捕獲和棧回退等各項(xiàng)重要工作都由它來(lái)完成。

__CxxRTThrowExp 首先接收(并保存)EXCEPTION 對(duì)象;然后從 TLS:Current ExpHdl 處找到與當(dāng)前函數(shù)對(duì)應(yīng)的 piHandler、nStep 等異常處理相關(guān)數(shù)據(jù);并按照前文所述的機(jī)制完成異常捕獲和棧回退。由此完成了包括“拋出”->“捕獲”->“回退”等步驟的整套異常處理機(jī)制。

看完上述內(nèi)容,你們掌握怎么在C++中實(shí)現(xiàn)異常機(jī)制的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道,感謝各位的閱讀!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站m.newbst.com,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

分享文章:怎么在C++中實(shí)現(xiàn)異常機(jī)制-創(chuàng)新互聯(lián)
文章URL:http://m.newbst.com/article48/dcihep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名虛擬主機(jī)網(wǎng)站收錄云服務(wù)器做網(wǎng)站移動(dòng)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名