[轉] c++ try catch 問題

windhaunting原文地址java

之前都是用try{} catch(…){}來捕獲C++中一些意想不到的異常, 今天看了Winhack的帖子才知道,這種方法在VC中實際上是靠不住的。例以下面的代碼:linux

    try
    {
    BYTE* pch ;
    pch = ( BYTE* )00001234 ;   //給予一個非法地址
    *pch = 6 ; //對非法地址賦值,會形成Access Violation 異常
    }
    catch(...)
    {
    AfxMessageBox( "catched" ) ;
    } 

這段代碼在debug下沒有問題,異常會被捕獲,會彈出」catched」的消息框。 但在Release方式下若是選擇了編譯器代碼優化選項,則VC編譯器會去搜索try塊中的代碼, 若是沒有找到throw代碼, 他就會認爲try catch結構是多餘的, 給優化掉。 這樣形成在Release模式下,上述代碼中的異常不能被捕獲,從而迫使程序彈出錯誤提示框退出。程序員

那麼可否在release代碼優化狀態下捕獲這個異常呢, 答案是有的。 就是__try, __except結構, 上述代碼若是改爲以下代碼異常便可捕獲。web

    __try
    {
    BYTE* pch ;
    pch = ( BYTE* )00001234 ;   //給予一個非法地址
    *pch = 6 ; //對非法地址賦值,會形成Access Violation 異常
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
    AfxMessageBox( "catched" ) ;
    } 

可是用__try, __except塊還有問題, 就是這個不是C++標準, 而是Windows平臺特有的擴展。 並且若是在使用過程當中涉及局部對象析構函數的調用,則會出現C2712 的編譯錯誤。 那麼還有沒有別的辦法呢?編程

固然有, 就是仍然使用C++標準的try{}catch(..){}, 但在編譯命令行中加入 /EHa 的參數。這樣VC編譯器不會把try catch模塊給優化掉了。小程序

找到一篇比較好的英文文章談這個問題: http://members.cox.net/doug_web/eh.htm數組

用C++10 年多了 , 竟然這麼基礎的問題都搞錯, 真是汗顏。 要加緊學習啊, Stay Hungry, Stay Foolish!安全

Written by oldmonk on 九月 11th, 2006 with 2 comments.
Read more articles on IT.
app

 


2. C++中catch(…)如何使用
上 一篇文章中詳細講了講C++異常處理模型的trycatch使用語法,其中catch關鍵字是用來定義catch block的,它後面帶一個參數,用來與異常對象的數據類型進行匹配。注意catch關鍵字只能定義一個參數,所以每一個catch block只能是一種數據類型的異常對象的錯誤處理模塊。若是要想使一個catch block能抓獲多種數據類型的異常對象的話,怎麼辦?C++標準中定義了一種特殊的catch用法,那就是」 catch(…)」。 框架

感性認識

一、catch(…)究竟是一個什麼樣的東東,先來個感性認識吧!看例子先:

 

int main()
{
try
{
cout << "在 try block 中, 準備拋出一個異常." << endl;
//這裏拋出一個異常(其中異常對象的數據類型是int,值爲1)
throw 1;
}
//catch( int& value )
//注意這裏catch語句
catch( …)
{
cout << "在 catch(…) block 中, 拋出的int類型的異常對象被處理" << endl;
}
}

二、哈哈!int類型的異常被catch(…)抓獲了,再來另外一個例子:

int main()
{
try
{
cout << "在 try block 中, 準備拋出一個異常." << endl;
//這裏拋出一個異常(其中異常對象的數據類型是double,值爲0.5)
throw 0.5;
}
//catch( double& value )
//注意這裏catch語句
catch( …)
{
cout << "在 catch(…) block 中, double類型的異常對象也被處理" << endl;
}
}

三、一樣,double類型的異常對象也被catch(…)塊抓獲了。是的,catch(..)能匹配成功全部的數據類型的異常對象,包括C++語言提 供全部的原生數據類型的異常對象,如int、double,還有char*、int*這樣的指針類型,另外還有數組類型的異常對象。同時也包括全部自定義的抽象數據類型。例程以下:

int main()
{
try
{
cout << "在 try block 中, 準備拋出一個異常." << endl;
//這裏拋出一個異常(其中異常對象的數據類型是char*)
char* p=0;
throw p;
}
//catch( char* value )
//注意這裏catch語句
catch( …)
{
cout << "在 catch(…) block 中, char*類型的異常對象也被處理" << endl;
}
}
//------------------------------------------------------------
int main() { try { cout << "在 try block 中, 準備拋出一個異常." << endl; //這裏拋出一個異常(其中異常對象的數據類型是int[]) int a[4]; throw a; } //catch( int value[] ) //注意這裏catch語句 catch( …) { cout << "在 catch(…) block 中, int[]類型的異常對象也被處理" << endl; } }

四、對於抽象數據類型的異常對象。catch(…)一樣有效,例程以下:

class MyException
{
public:
protected:
int code;
};

int main()
{
try
{
cout << "在 try block 中, 準備拋出一個異常." << endl;
//這裏拋出一個異常(其中異常對象的數據類型是MyException)
throw MyException();
}
//catch(MyException& value )
//注意這裏catch語句
catch( …)
{
cout << "在catch(…) block中, MyException類型的異常對象被處理" << endl;
}
}

對catch(…)有點迷糊?
一、究竟對catch(…)有什麼迷糊呢?仍是看例子先吧!

void main()
{
int* p = 0;

try
{
// 注意:下面這條語句雖然不是throw語句,但它在執行時會致使系統
// 出現一個存儲保護錯誤的異常(access violation exception)
*p = 13; // causes an access violation exception;
}
catch(...)
{
//catch(…)能抓獲住上面的access violation exception異常嗎?
cout << "在catch(…) block中" << endl;
}
}

  請問上面的程序運行時會出現什麼結果嗎?catch(…)能抓獲住系統中出現的access violation exception異常嗎?朋友們!和咱們的主人公阿愚同樣,本身動手去測試一把!
結 果又如何呢?實際上它有兩種不一樣的運行結果,在window2000系統下用VC來測試運行這個小程序時,發現程序能輸出"在catch(…) block中"的語句在屏幕上,也即catch(…) 能成功抓獲住系統中出現的access violation exception異常,很厲害吧!但若是這個一樣的程序在linux下用gcc編譯後運行時,程序將會出現崩潰,並在屏幕上輸出」segment fault」的錯誤信息。

主人公阿愚有點急了,也開始有點迷糊了,爲何?爲何?爲何一樣一個程序在兩種不一樣的系統上有不一樣的表現 呢?其緣由就是:對於這種因爲硬件或操做 系統出現的系統異常(例如說被零除、內存存儲控制異常、頁錯誤等等)時,window2000系統有一個叫作結構化異常處理(Structured Exception Handling,SEH)的機制,這個東東太厲害了,它能和VC中的C++異常處理模型很好的結合上(實際上VC實現的C++異常處理模型很大程度上建 立在SEH機制之上的,或者說它是SEH的擴展,後面文章中會詳細闡述並分析這個久富盛名的SEH,看看catch(…)是如何神奇接管住這種系統異常出 現後的程序控制流的,不過這都是後話)。而在linux系統下,系統異常是由信號處理編程方法來控制的(信號處理編程,signal processing progamming。在介紹unix和linux下如何編程的書籍中,都會有對信號處理編程詳細的介紹,固然執著的主人公阿愚確定對它也不會放過,會深 入到unix沿襲下來的信號處理編程內部的實現機制,並嘗試完善改進它,使它也可以較好地和C++異常處理模型結合上)。

那麼C++標 準中對於這種同一個程序有不一樣的運行結果有何解釋呢?這裏須要注意的是,window2000系統下catch(…)能捕獲住系統異常, 這徹底是它本身的擴展。在C++標準中並無要求到這一點,它只規定catch(…)必須能捕獲程序中全部經過throw語句拋出的異常。所以上面的這個 程序在linux系統下的運行結果也徹底是符合C++標準的。雖然你們也必須認可window2000系統下對C++異常處理模型的這種擴展確實是一個很 不錯的完善,極大得提升了程序的安全性。

爲何要用catch(…)這個東東?

程序員朋友們也許會說,這還有問嗎?這篇文章的一開始不就講到了嗎?catch(…)可以捕 獲多種數據類型的異常對象,因此它提供給程序員一種對異常 對象更好的控制手段,使開發的軟件系統有很好的可靠性。所以一個比較有經驗的程序員一般會這樣組織編寫它的代碼模塊,以下:

void Func()
{
try
{
// 這裏的程序代碼完成真正複雜的計算工做,這些代碼在執行過程當中
// 有可能拋出DataType一、DataType2和DataType3類型的異常對象。
}
catch(DataType1& d1)
{
}
catch(DataType2& d2)
{
}
catch(DataType3& d3)
{
}
// 注意上面try block中可能拋出的DataType一、DataType2和DataType3三
// 種類型的異常對象在前面都已經有對應的catch block來處理。但爲何
// 還要在最後再定義一個catch(…) block呢?這就是爲了有更好的安全性和
// 可靠性,避免上面的try block拋出了其它未考慮到的異常對象時致使的程
// 序出現意外崩潰的嚴重後果,並且這在用VC開發的系統上更特別有效,因
// 爲catch(…)能捕獲系統出現的異常,而系統異常每每令程序員頭痛了,現
// 在系統通常都比較複雜,並且由不少人共同開發,一不當心就會致使一個
// 指針變量指向了其它非法區域,結果意外災難不幸發生了。catch(…)爲這種
// 潛在的隱患提供了一種有效的補救措施。
catch(…)
{
}
}

還有,特別是VC程序員爲了使開發的系統有更好的可靠性,每每在應用程序的入口函數中(如MFC框架的開發環境下 CXXXApp::InitInstance())和工做線程的入口函數中加上一個頂層的trycatch塊,而且使用catch(…)來捕獲一切全部的 異常,以下:

BOOL CXXXApp::InitInstance()
{
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}

AfxEnableControlContainer();

// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif


// 注意這裏有一個頂層的trycatch塊,而且使用catch(…)來捕獲一切全部的異常
try
{
CXXXDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
}
catch(…)
{
// dump出系統的一些重要信息,並通知管理員查找出現意外異常的緣由。
// 同時想辦法恢復系統,例如說從新啓動應用程序等
}

// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}

   經過上面的例程和分析能夠得出,因爲catch(…)可以捕獲全部數據類型的異常對象,因此在恰當的地方使用catch(…)確實可使軟件系統有着更 好的可靠性。這確實是你們使用catch(…)這個東東最好的理由。但不要誤會的是,在C++異常處理模型中,不僅有catch(…)方法可以捕獲幾乎所 有類型的異常對象(也許有其它更好的方法,在下一篇文章中主人公阿愚帶你們一同去探討一下),可C++標準中爲何會想到定義這樣一個catch(…) 呢?有過java或C#編程開發經驗的程序員會發現,在它們的異常處理模型中,並無這樣相似的一種語法,可這裏不得再也不次強調的是,java中的異常處 理模型是C++中的異常處理模型的完善改進版,可它反而沒有了catch(…),爲什麼呢?仍是先去看看下一章吧,「C++的異常處理和麪向對象的緊密關係 」。也許你們能找到一個彷佛合理的緣由。

相關文章
相關標籤/搜索