一個(gè)異常被拋出時(shí),就會(huì)立即引發(fā) C++ 的異常捕獲機(jī)制:
制.png) 圖4 C++ 異常捕獲機(jī)制
在上一小節(jié)中,我們已經(jīng)看到了 nStep 變量在跟蹤對(duì)象構(gòu)造、析構(gòu)方面的作用。實(shí)際上 nStep 除了能夠跟蹤對(duì)象創(chuàng)建、銷(xiāo)毀階段以外,還能夠標(biāo)識(shí)當(dāng)前執(zhí)行點(diǎn)是否在 try 塊中,以及(如果當(dāng)前函數(shù)有多個(gè) try 塊的話(huà))究竟在哪個(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 塊所能捕獲的異常類(lèi)型及其起始地址等信息。
若找到了一個(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ì)于后者,我們稱(chēng)為發(fā)生了未捕獲的異常,對(duì)于 C++ 異常處理器而言,未捕獲的異常是一個(gè)嚴(yán)重錯(cuò)誤,將導(dǎo)致當(dāng)前進(jìn)程被強(qiáng)制結(jié)束。
注意:雖然在圖4示例中的 tblTryBlocks[] 只有一個(gè)條目,這個(gè)條目中的 tblCatchBlocks[] 也只有一行。但是在實(shí)際情況中,這兩個(gè)表中都允許有多條記錄。意即:一個(gè)函數(shù)中可以有多個(gè) try 塊,每個(gè) try 塊后均可跟隨多個(gè)與之配套的 catch 塊。
注意:按照標(biāo)準(zhǔn)意義上的理解,異常時(shí)的棧回退是伴隨著異常捕獲過(guò)程沿著異常處理鏈逐層向上進(jìn)行的。但是有些編譯器是在先完成異常捕獲后再一次性進(jìn)行棧回退的。無(wú)論具體實(shí)現(xiàn)使用了哪種方式,除非正在開(kāi)發(fā)一個(gè)內(nèi)存嚴(yán)格受限的嵌入式應(yīng)用,通常我們按照標(biāo)準(zhǔn)語(yǔ)意來(lái)理解都不會(huì)產(chǎn)生什么問(wèn)題。
備注:實(shí)際上 tblCatchBlocks 中還有一些較為關(guān)鍵但被故意省略的字段。比如指明該 catch 塊異常對(duì)象復(fù)制方式(傳值(拷貝構(gòu)造)或傳址(引用或指針))的字段,以及在何處存放被復(fù)制的異常對(duì)象(相對(duì)于入口地址的偏移位置)等信息。 |