解析C++編程中異常相關(guān)的堆棧展開和throw()異常規(guī)范
C++ 中的異常和堆棧展開
在 C++ 異常機制中,控制從 throw 語句移至可處理引發(fā)類型的第一個 catch 語句。在到達 catch 語句時,throw 語句和 catch 語句之間的范圍內(nèi)的所有自動變量將在名為“堆棧展開”的過程中被銷毀。在堆棧展開中,執(zhí)行將繼續(xù),如下所示:
控制通過正常順序執(zhí)行到達 try 語句。執(zhí)行 try 塊內(nèi)的受保護部分。
如果執(zhí)行受保護的部分的過程中未引發(fā)異常,將不會執(zhí)行 try 塊后面的 catch 子句。執(zhí)行將在關(guān)聯(lián)的 try 塊后的最后一個 catch 子句后面的語句上繼續(xù)。
如果執(zhí)行受保護部分的過程中或在受保護的部分調(diào)用的任何例程中引發(fā)異常(直接或間接),則從通過 throw 操作數(shù)創(chuàng)建的對象中創(chuàng)建異常對象。(這意味著,可能會涉及復(fù)制構(gòu)造函數(shù)。)此時,編譯器會在權(quán)限更高的執(zhí)行上下文中查找可處理引發(fā)的類型異常的 catch 子句,或查找可以處理任何類型的異常的 catch 處理程序。按照 catch 處理程序在 try 塊后面的顯示順序檢查這些處理程序。如果未找到適當?shù)奶幚沓绦?,則檢查下一個動態(tài)封閉的 try 塊。此過程將繼續(xù),直到檢查最外面的封閉 try 塊。
如果仍未找到匹配的處理程序,或者在展開過程中但在處理程序獲得控制前發(fā)生異常,則調(diào)用預(yù)定義的運行時函數(shù) terminate。如果在引發(fā)異常后但在展開開始前發(fā)生異常,則調(diào)用 terminate。
如果找到匹配的 catch 處理程序,并且它通過值進行捕獲,則通過復(fù)制異常對象來初始化其形參。如果它通過引用進行捕獲,則初始化參數(shù)以引用異常對象。在初始化形參后,堆棧的展開過程將開始。這包括對與 catch 處理程序關(guān)聯(lián)的 try 塊的開頭和異常的引發(fā)站點之間完全構(gòu)造(但尚未析構(gòu))的所有自動對象的析構(gòu)。析構(gòu)按照與構(gòu)造相反的順序發(fā)生。執(zhí)行 catch 處理程序且程序會在最后一個處理程序之后(即,在不是 catch 處理程序的第一個語句或構(gòu)造處)恢復(fù)執(zhí)行??刂浦荒芡ㄟ^引發(fā)的異常進入 catch 處理程序,而絕不會通過 goto 語句或 switch 語句中的 case 標簽進入。
堆棧展開示例
以下示例演示引發(fā)異常時如何展開堆棧。線程執(zhí)行將從 C 中的 throw 語句跳轉(zhuǎn)到 main 中的 catch 語句,并在此過程中展開每個函數(shù)。請注意創(chuàng)建 Dummy 對象的順序,并且會在它們超出范圍時將其銷毀。還請注意,除了包含 catch 語句的 main 之外,其他函數(shù)均未完成。函數(shù) A 絕不會從其對 B() 的調(diào)用返回,并且 B 絕不會從其對 C() 的調(diào)用返回。如果取消注釋 Dummy 指針和相應(yīng)的 delete 語句的定義并運行程序,請注意絕不會刪除該指針。這說明了當函數(shù)不提供異常保證時會發(fā)生的情況。有關(guān)詳細信息,請參閱“如何:針對異常進行設(shè)計”。如果注釋掉 catch 語句,則可以觀察當程序因未經(jīng)處理的異常而終止時將發(fā)生的情況。
#include <string>
#include <iostream>
using namespace std;
class MyException{};
class Dummy
{
public:
Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
~Dummy(){ PrintMsg("Destroyed Dummy:"); }
void PrintMsg(string s) { cout << s << MyName << endl; }
string MyName;
int level;
};
void C(Dummy d, int i)
{
cout << "Entering FunctionC" << endl;
d.MyName = " C";
throw MyException();
cout << "Exiting FunctionC" << endl;
}
void B(Dummy d, int i)
{
cout << "Entering FunctionB" << endl;
d.MyName = "B";
C(d, i + 1);
cout << "Exiting FunctionB" << endl;
}
void A(Dummy d, int i)
{
cout << "Entering FunctionA" << endl;
d.MyName = " A" ;
// Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
B(d, i + 1);
// delete pd;
cout << "Exiting FunctionA" << endl;
}
int main()
{
cout << "Entering main" << endl;
try
{
Dummy d(" M");
A(d,1);
}
catch (MyException& e)
{
cout << "Caught an exception of type: " << typeid(e).name() << endl;
}
cout << "Exiting main." << endl;
char c;
cin >> c;
}
輸出:Entering main Created Dummy: M Copy created Dummy: M Entering FunctionA Copy created Dummy: A Entering FunctionB Copy created Dummy: B Entering FunctionC Destroyed Dummy: C Destroyed Dummy: B Destroyed Dummy: A Destroyed Dummy: M Caught an exception of type: class MyException Exiting main.
異常規(guī)范 (throw)
異常規(guī)范是在 C++11 中棄用的 C++ 語言功能。這些規(guī)范原本用來提供有關(guān)可從函數(shù)引發(fā)哪些異常的摘要信息,但在實際應(yīng)用中發(fā)現(xiàn)這些規(guī)范存在問題。證明確實有一定用處的一個異常規(guī)范是 throw() 規(guī)范。例如:
void MyFunction(int i) throw();
告訴編譯器函數(shù)不引發(fā)任何異常。它相當于使用 __declspec(nothrow)。這種用法是可選的。
(C++11) 在 ISO C++11 標準中,引入了 noexcept 運算符,該運算符在 Visual Studio 2015 及更高版本中受支持。盡可能使用 noexcept 指定函數(shù)是否可能會引發(fā)異常。
Visual C++ 中實現(xiàn)的異常規(guī)范與 ISO C++ 標準有所不同。下表總結(jié)了 Visual C++ 的異常規(guī)范實現(xiàn):
| 異常規(guī)范 | 含義 |
|---|---|
| throw() | 函數(shù)不會引發(fā)異常。但是,如果從標記為 throw() 函數(shù)引發(fā)異常,Visual C++ 編譯器將不會調(diào)用意外處理函數(shù)。如果使用 throw() 標記一個函數(shù),則 Visual C++ 編譯器假定該函數(shù)不會引發(fā) C++ 異常,并相應(yīng)地生成代碼。由于 C++ 編譯器可能會執(zhí)行代碼優(yōu)化(基于函數(shù)不會引發(fā)任何 C++ 異常的假設(shè)),因此,如果函數(shù)引發(fā)異常,則程序可能無法正確執(zhí)行。 |
| throw(...) | 函數(shù)可以引發(fā)異常。 |
| throw(type) | 函數(shù)可以引發(fā) type 類型的異常。但是,在 Visual C++ .NET 中,這被解釋為 throw(...)。 |
如果在應(yīng)用程序中使用異常處理,則一定有一個或多個函數(shù)處理引發(fā)的異常。在引發(fā)異常的函數(shù)和處理異常的函數(shù)間調(diào)用的所有函數(shù)必須能夠引發(fā)異常。
函數(shù)的引發(fā)行為基于以下因素:
- 您是否在 C 或 C++ 下編譯函數(shù)。
- 您所使用的 /EH 編譯器選項。
- 是否顯式指定異常規(guī)范。
不允許對 C 函數(shù)使用顯式異常規(guī)范。
下表總結(jié)了函數(shù)的引發(fā)行為:
// exception_specification.cpp
// compile with: /EHs
#include <stdio.h>
void handler() {
printf_s("in handler\n");
}
void f1(void) throw(int) {
printf_s("About to throw 1\n");
if (1)
throw 1;
}
void f5(void) throw() {
try {
f1();
}
catch(...) {
handler();
}
}
// invalid, doesn't handle the int exception thrown from f1()
// void f3(void) throw() {
// f1();
// }
void __declspec(nothrow) f2(void) {
try {
f1();
}
catch(int) {
handler();
}
}
// only valid if compiled without /EHc
// /EHc means assume extern "C" functions don't throw exceptions
extern "C" void f4(void);
void f4(void) {
f1();
}
int main() {
f2();
try {
f4();
}
catch(...) {
printf_s("Caught exception from f4\n");
}
f5();
}
輸出:
About to throw 1 in handler About to throw 1 Caught exception from f4 About to throw 1 in handler
欄 目:C語言
下一篇:詳解C++編程中向函數(shù)傳遞引用參數(shù)的用法
本文標題:解析C++編程中異常相關(guān)的堆棧展開和throw()異常規(guī)范
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2516.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計- 解析最少換車次數(shù)的問題詳解
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10深入解析最長公共子串
- 01-10c++中inline的用法分析
- 01-10用C++實現(xiàn)DBSCAN聚類算法
- 01-10全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關(guān)鍵字的使用詳解


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實例總結(jié)
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個骰子


