C++異常處理機制幾種方法

1、異常java

  迄今爲止,咱們處理程序中的錯誤通常都是用if語句測試某個表達式,而後處理錯誤的特定義代碼。 c++

 

 

C++異常機制使用了三個新的關鍵字  (SEH(結構化異常處理))程序員

try    ──標識可能出現的異常代碼段shell

throw  ──拋出一個異常編程

catch  ──標識處理異常的代碼段windows

 

提示:數組

 使用異常處理將帶來更多的系統開銷。所以慎用異常。數據結構

2、拋出異常ide

throw函數

throw必須在 try代碼塊中.後邊跟的值決定拋出異常的類型。

3、捕獲異常

catch  

出如今try代碼塊後,後邊跟的數據決定捕獲的類型

catch(...) //表示捕獲全部異常

int _tmain(int argc, _TCHAR* argv[])

     int a,b;

     a=333;

     b=0;

     try

     {

         if (b==0)

         {

             //錯誤處理

             throw "出錯,除數爲0了";

            // throw 111.0;

         }

         printf("%d",a/b);

     }

     catch(char *s)

     {

       //錯誤處理

     }

     catch (int i)

     {

         //整型錯誤代碼 處理

     }

     catch(...)

     {

         //全部異常類型

     }

     

    getchar();

    return 0;

}

 

 

C++ - C++ signal的使用

 

1. 頭文件
#include <signal.h>


2. 功能
設置某一信號的對應動做


3. 函數原型
void (*signal(int signum,void(* handler)(int)))(int);

   分解來看:
   typedef void (*sig_t) (int);
   sig_t signal(int sig, sig_t func);
   第一個參數是目標信號。func參數是一個指針,指向某個處理該信號的函數。這個處理信號函數帶有一個int型參數,並應返回void
   func參數也能夠設定爲下面的一些值:
   SIG_IGN: 若是func參數被設置爲SIG_IGN,該信號將被忽略。
   SIG_DFL: 若是func參數被設置爲SIG_DFL,該信號會按照肯定行爲處理。


4. sig信號的可能類型
1) #define SIGHUP 1 /* hangup */
   SIGHUPUnix系統管理員很經常使用的一個信號。許多後臺服務進程在接受到該信號後將會從新讀取它們的配置文件。然而,該信號的實際功能是通知進程它的控制終端被斷開。缺省行爲是終止進程。


2) #define SIGINT 2 /* interrupt */
   對於Unix使用者來講,SIGINT是另一個經常使用的信號。許多shellCTRL-C組合使得這個信號被你們所熟知。該信號的正式名字是中斷信號。缺省行爲是終止進程。

 

3) #define SIGQUIT 3 /* quit */

   SIGQUIT信號被用於接收shellCTRL-/組合。另外,它還用於告知進程退出。這是一個經常使用信號,用來通知應用程序從容的(譯註:即在結束前執行一些退出動做)關閉。缺省行爲是終止進程,而且建立一個核心轉儲。


4) #define SIGILL 4 /* illegal instr. (not reset when caught) */
   若是正在執行的進程中包含非法指令,操做系統將向該進程發送SIGILL信號。若是你的程序使用了線程,或者pointer functions,那麼可能的話能夠嘗試捕獲該信號來協助調試。([color=Red]注意:原文這句爲:「If your program makes use of use of threads, or pointer functions, try to catch this signal if possible for aid in debugging.」。中間的兩個use of use of,不知是原書排版的瑕疵仍是我確實沒有明白其意義;另外,偶常常據說functions pointer,對於pointer functionsgoogle了一下,應該是fortran裏面的東西,無論怎樣,還真不知道,確切含義還請知道的兄弟斧正。[/color])缺省行爲是終止進程,而且建立一個核心轉儲。


5) #define SIGTRAP 5 /* trace trap (not reset when caught) */
   SIGTRAP這個信號是由POSIX標準定義的,用於調試目的。當被調試進程接收到該信號時,就意味着它到達了某一個調試斷點。一旦這個信號被交付,被調試的進程就會中止,而且它的父進程將接到通知。缺省行爲是終止進程,而且建立一個核心轉儲。


6) #define SIGABRT 6 /* abort() */
   SIGABRT提供了一種在異常終止(abort)一個進程的同時建立一個核心轉儲的方法。然而若是該信號被捕獲,而且信號處理句柄沒有返回,那麼進程不會終止。缺省行爲是終止進程,而且建立一個核心轉儲。


7) #define SIGFPE 8 /* floating point exception */
   當進程發生一個浮點錯誤時,SIGFPE信號被髮送給該進程。對於那些處理複雜數學運算的程序,通常會建議你捕獲該信號。缺省行爲是終止進程,而且建立一個核心轉儲。


8) #define SIGKILL 9 /* kill (cannot be caught or ignored) */
   SIGKILL是這些信號中最難對付的一個。正如你在它旁邊的註釋中看到的那樣,這個信號不能被捕獲或忽略。一旦該信號被交付給一個進程,那麼這個進程就會終止。然而,會有一些極少數狀況SIGKILL不會終止進程。這些罕見的情形在處理一個非中斷操做(好比磁盤I/O)的時候發生。雖然這樣的情形極少發生,然而一旦發生的話,會形成進程死鎖。惟一結束進程的辦法就只有從新啓動了。缺省行爲是終止進程。


9) #define SIGBUS 10 /* bus error */
   如同它的名字暗示的那樣,CPU檢測到數據總線上的錯誤時將產生SIGBUS信號。當程序嘗試去訪問一個沒有正確對齊的內存地址時就會產生該信號。缺省行爲是終止進程,而且建立一個核心轉儲。

 

10) #define SIGSEGV 11 /* segmentation violation */
   SIGSEGV是另外一個C/C++程序員很熟悉的信號。當程序沒有權利訪問一個受保護的內存地址時,或者訪問無效的虛擬內存地址(髒指針,dirty pointers,譯註:因爲沒有和後備存儲器中內容進行同步而形成。關於野指針,能夠參見http://en.wikipedia.org/wiki/Wild_pointer 的解釋。)時,會產生這個信號。缺省行爲是終止進程,而且建立一個核心轉儲。


11) #define SIGSYS 12 /* non-existent system call invoked */
   SIGSYS信號會在進程執行一個不存在的系統調用時被交付。操做系統會交付該信號,而且進程會被終止。缺省行爲是終止進程,而且建立一個核心轉儲。


12) #define SIGPIPE 13 /* write on a pipe with no one to read it */
   管道的做用就像電話同樣,容許進程之間的通訊。若是進程嘗試對管道執行寫操做,然而管道的另外一邊卻沒有迴應者時,操做系統會將SIGPIPE信號交付給這個討厭的進程(這裏就是那個打算寫入的進程)。缺省行爲是終止進程。


13) #define SIGALRM 14 /* alarm clock */
   在進程的計時器到期的時候,SIGALRM信號會被交付(delivered)給進程。這些計時器由本章後面將會說起
setitimeralarm調用設置。缺省行爲是終止進程。


14) #define SIGTERM 15 /* software termination signal from kill */
   SIGTERM信號被髮送給進程,通知該進程是時候終止了,而且在終止以前作一些清理活動。SIGTERM信號是Unixkill命令發送的缺省信號,同時也是操做系統關閉時向進程發送的缺省信號。缺省行爲是終止進程。


15) #define SIGURG 16 /* urgent condition on IO channel */
   在進程已打開的套接字上發生某些狀況時,SIGURG將被髮送給該進程。若是進程不捕獲這個信號的話,那麼將被丟棄。缺省行爲是丟棄這個信號。


16) #define SIGSTOP 17 /* sendable stop signal not from tty */
   本信號不能被捕獲或忽略。一旦進程接收到SIGSTOP信號,它會當即中止(stop),直到接收到另外一個SIGCONT
信號爲止。缺省行爲是中止進程,直到接收到一個SIGCONT信號爲止。


17) #define SIGTSTP 18 /* stop signal from tty */
   SIGSTPSIGSTOP相似,它們的區別在於SIGSTP信號能夠被捕獲或忽略。當shell從鍵盤接收到CTRL-Z的時候就會交付(deliver)這個信號給進程。缺省行爲是中止進程,直到接收到一個SIGCONT信號爲止。


18) #define SIGCONT 19 /* continue a stopped process */
   SIGCONT也是一個有意思的信號。如前所述,當進程中止的時候,這個信號用來告訴進程恢復運行。該信號的有趣的地方在於:它不能被忽略或阻塞,但能夠被捕獲。這樣作頗有意義:由於進程大概不肯意忽略或阻塞SIGCONT信號,不然,若是進程接收到SIGSTOPSIGSTP的時候該怎麼辦?缺省行爲是丟棄該信號。


19) #define SIGCHLD 20 /* to parent on child stop or exit */
   SIGCHLD是由Berkeley Unix引入的,而且比SRV 4 Unix上的實現有更好的接口。(若是信號是一個沒有追溯能力的過程(not a retroactive process),那麼BSDSIGCHID信號實現會比較好。在system V Unix的實現中,若是進程要求捕獲該信號,操做系統會檢查是否存在有任何未完成的子進程(這些子進程是已經退出exit)的子進程,而且在等待調用wait的父進程收集它們的狀態)。若是子進程退出的時候附帶有一些終止信息(terminating information),那麼信號處理句柄就會被調用。因此,僅僅要求捕獲這個信號會致使信號處理句柄被調用(譯註:便是上面說的信號的追溯能力」),而這是卻一種至關混亂的情況。)一旦一個進程的子進程狀態發生改變,SIGCHLD信號就會被髮送給該進程。就像我在前面章節提到的,父進程雖然能夠fork出子進程,但沒有必要等待子進程退出。通常來講這是不太好的,由於這樣的話,一旦進程退出就可能會變成一個殭屍進程。但是若是父進程捕獲SIGCHLD信號的話,它就可使用wait系列調用中的某一個去收集子進程狀態,或者判斷髮生了什麼事情。當發送SIGSTOP,SIGSTPSIGCONF信號給子進程時,SIGCHLD信號也會被髮送給父進程。缺省行爲是丟棄該信號。


20) #define SIGTTIN 21 /* to readers pgrp upon background tty read */
   當一個後臺進程嘗試進行一個讀操做時,SIGTTIN信號被髮送給該進程。進程將會阻塞直到接收到SIGCONT信號爲止。缺省行爲是中止進程,直到接收到SIGCONT信號。


21) #define SIGTTOU 22 /* like TTIN if (tp->t_local<OSTOP) */
   SIGTTOU信號與SIGTTIN很類似,不一樣之處在於SIGTTOU信號是因爲後臺進程嘗試對一個設置了TOSTOP屬性的tty執行寫操做時纔會產生。然而,若是tty沒有設置這個屬性,SIGTTOU就不會被髮送。缺省行爲是中止進程,直到接收到SIGCONT信號。


22) #define SIGIO 23 /* input/output possible signal */
   若是進程在一個文件描述符上有I/O操做的話,SIGIO信號將被髮送給這個進程。進程能夠經過fcntl調用來設置。缺省行爲是丟棄該信號。


23) #define SIGXCPU 24 /* exceeded CPU time limit */
   若是一旦進程超出了它可使用的CPU限制(CPU limit),SIGXCPU信號就被髮送給它。這個限制可使用隨後討論的setrlimit設置。缺省行爲是終止進程。


24) #define SIGXFSZ 25 /* exceeded file size limit */
   若是一旦進程超出了它可使用的文件大小限制,SIGXFSZ信號就被髮送給它。稍後咱們會繼續討論這個信號。缺省行爲是終止進程。


25) #define SIGVTALRM 26 /* virtual time alarm */
   若是一旦進程超過了它設定的虛擬計時器計數時,SIGVTALRM信號就被髮送給它。缺省行爲是終止進程。


26) #define SIGPROF 27 /* profiling time alarm */
   當設置了計時器時,SIGPROF是另外一個將會發送給進程的信號。缺省行爲是終止進程。

27) #define SIGWINCH 28 /* window size changes */
   當進程調整了終端的行或列時(好比增大你的xterm的尺寸),SIGWINCH信號被髮送給該進程。缺省行爲是丟棄該信號。


28) #define SIGUSR1 29 /* user defined signal 1 */
29) #define SIGUSR2 30 /* user defined signal 2 */
   SIGUSR1SIGUSR2這兩個信號被設計爲用戶指定。它們能夠被設定來完成你的任何須要。換句話說,操做系統沒有任何行爲與這兩個信號關聯。缺省行爲是終止進程。(譯註:按原文的意思翻譯出來彷佛這兩句話有點矛盾。)

5. 例子
   5.1. Linux下的Ctrl+CWindows下的實現一
   Linux下一般的作法:
   signal(SIGINT, sigfunc); // 設置信號
   void sigfunc(int signo)
   {
      ... //處理信號相關的操做
   }

   如下是Linux下的Ctrl+CWindows下的實現
   #include <stdio.h>
   #include <windows.h>
   static is_loop = 1;
   // 捕獲控制檯 Ctrl+C 事件的函數
   BOOL CtrlHandler( DWORD fdwCtrlType )
   {
      switch (fdwCtrlType)
      {
      /* Handle the CTRL-C signal. */
      case CTRL_C_EVENT:
         printf("CTRL_C_EVENT \n");
         break;
      case CTRL_CLOSE_EVENT:
         printf("CTRL_CLOSE_EVENT \n");
         break;
      case CTRL_BREAK_EVENT:
         printf("CTRL_BREAK_EVENT \n");
         break;
      case CTRL_LOGOFF_EVENT:
         printf("CTRL_LOGOFF_EVENT \n");
         break;
      case CTRL_SHUTDOWN_EVENT:
         printf("CTRL_SHUTDOWN_EVENT \n");
         break;
      default:
         return FALSE;
      }
      is_loop = 0;
      return (TRUE);
   }


   int main(int argc, char *argv[])
   {
      printf("Set Console Ctrl Handler\n");
      SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
      while (is_loop);
      return 0;
   }


   5.2.Linux下的Ctrl+CWindows下的實現二
   #include <stdio.h>
   #include <windows.h>
   #define CONTRL_C_HANDLE() signal(3, exit)
   int main(int argc, char *argv[])
   {
      printf("Set Console Ctrl Handler\n");
      CONTRL_C_HANDLE();
      while (1);
      system("PAUSE");
      return 0;
   }

 

 

windows異常處理 __try __except

try-except用法

  try exceptwindows 系統獨有的異常處理模型,windows的異常處理模式,稱爲SEH( structured exception handling )

       SEH的異常處理模型主要由try-except語句來完成,與標準的try catch類似。與C++異常處理模型使用catch關鍵字來定義異常處理模塊,而SEH是採用__except關鍵

字來定義。而且,catch關鍵字後面每每好像接受一個函數參數同樣,能夠是各類類型的異常數據對象;可是__except關鍵字則不一樣,它後面跟的倒是一個表達式.

咱們知道,函數調用也是一個表達式。

    咱們來看下面這個例子,這個例子是用來處理棧溢出的異常。    

long WINAPI FilterFunc(DWORD dwExceptionCode)

{

return (dwExceptionCode == STATUS_STACK_OVERFLOW) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;

}

UINT WINAPI ThreadFunc(LPVOID param)

{

__try

{

                   // guarded code 

}

__except (FilterFunc(GetExceptionCode()))

{

// 若是是棧溢出,進行處理。

}

 

    return  TRUEt;    

}

except參數的值有如下三種:

       EXCEPTION_CONTINUE_EXECUTION (–1)     異常被忽略,控制流將在異常出現的點以後,繼續恢復運行。

  EXCEPTION_CONTINUE_SEARCH (0)          異常不被識別,也即當前的這個__except模塊不是這個異常錯誤所對應的正確的異常處理模塊。系統將繼續到上一try-

except域中繼續查找一個恰當的__except模塊。

  EXCEPTION_EXECUTE_HANDLER (1)         異常已經被識別,控制流將進入到__except模塊中運行異常處理代碼

try-except的關鍵是如何在__except模塊中得到異常錯誤的相關信息.

Windows提供了兩個API函數來獲取異常信息:

LPEXCEPTION_POINTERS GetExceptionInformation(VOID); //取得異常相關信息

DWORD GetExceptionCode(VOID); // 取得異常編號

GetExceptionCode()返回異常編號,而GetExceptionInformation()返回更豐富的信息,EXCEPTION_POINTERS結構以下,

typedef struct _EXCEPTION_POINTERS { // exp 

PEXCEPTION_RECORD ExceptionRecord; 

PCONTEXT ContextRecord; 

} EXCEPTION_POINTERS;

其中EXCEPTION_RECORD類型,它記錄了一些與異常相關的信息;而CONTEXT數據結構體中記錄了異常發生時,線程當時的上下文環境,主要包括寄存器的值。

有了這些信息,__except模塊即可以對異常錯誤進行很好的分類和恢復處理,一般咱們須要一個過濾函數來輔助。通常稱爲是filterfunction.過濾函數只過濾須要處

理的異常。

int exception_access_violation_filter(LPEXCEPTION_POINTERS p_exinfo)

{

    if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)

    {

        messagebox("access vialation exceptionn");

       return EXCEPTION_EXECUTE_HANDLER ; //告訴except處理這個異常

    }

    else return EXCEPTION_CONTINUE_SEARCH; //不告訴except處理這個異常

}

int exception_int_divide_by_zero_filter(LPEXCEPTION_POINTERS p_exinfo)

{

    if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)

    {

        return  EXCEPTION_EXECUTE_HANDLER; //告訴except處理這個異常

    }

   else return EXCEPTION_CONTINUE_SEARCH; //不告訴except處理這個異常

}

因而,你能夠這樣寫這段異常處理代碼:

__try

{

  // guarded code

}

__except(exception_access_violation_filter(GetExceptionInformation()))

{

//

}

__try

{

  // guarded code

}

__exceptexception_int_divide_by_zero_filter(GetExceptionInformation()))

{

//exception handling

}

SEH異常處理模型中,也能夠拋出一個異常。對應的WindowsAPI函數是RaiseException,

VOID RaiseException(

DWORD dwExceptionCode, // 異常的編號

DWORD dwExceptionFlags, // 異常標記

DWORD nNumberOfArguments, // 參數個數

CONST DWORD *lpArguments //  參數數組首地址

);

一般,後三個參數基本不用

SEH異常處理還有try-finally.相似於java裏的try-catch-finally.可是SEHtry只能和exceptfinally二者之間的一個搭配,不能有try-except-finnaly. 

 C++異常模型用try-catch語法定義,而SEH異常模型則用try-except語法,C++異常模型類似,try-except也支持多層的try-except嵌套。

 try-except模型中,一個try塊只能是有一個except塊;而C++異常模型中,一個try塊能夠有多個catch塊。

 C++異常模型是按照異常對象的類型來進行匹配查找的;而try-except模型則不一樣,它經過一個表達式的值來進行判斷.

  __except關鍵字後面跟的表達式,它能夠是各類類型的表達式,例如,它能夠是一個函數調用,或是一個條件表達式,或是一個逗號表達式,或乾脆就是一個整

型常量等等。最經常使用的是一個函數表達式,而且經過利用GetExceptionCode()GetExceptionInformation ()函數來獲取當前的異常錯誤信息,便於程序員有效控制異常

錯誤的分類處理。

SEH異常處理模型中,異常經過RaiseException()函數拋出。RaiseException()函數的做用相似於C++異常模型中的throw

關於SEH異常處理更詳細的資料,你能夠去看windows via c/c++這本書,中文譯名是windows核心編程。不過仍是建議你看英文原版,翻譯的版本質量不高。

 

C語言中出現內存不可讀寫錯誤,如何不讓這個異常終止程序?

最後使用 __try __exception 捕獲了這個異常。

__try

{

}

__exception(EXCEPTION_EXECUTE_HANDLER)

{

}


error C2712: 沒法在要求對象展開的函數中使用 __try

 

[cpp]  view plain

 

  1. bool WindowContainer::GotoMainPage(bool bDestroyCurWndPage/* = true*/)  
  2. {  
  3.     bool bResult = false;  
  4.   
  5.     LockChilds();  
  6.   
  7.     __try  
  8.     {  
  9.         <span style="color:#cc0000;">GUIList::iterator it = m_lstWndPage.begin();// 這句話報錯</span><span style="color:#cc0000;">消息                 </span>  

 

[cpp]  view plain
  1. <span style="color:#cc0000;">                   </span><span style="color:#006600;">if(it != m_lstWndPage.end() && (*it) != m_pCurPage)  
  2.         {  
  3.             WndPage * pCurPage = m_pCurPage;  
  4.   
  5.             SwitchToWndPage((WndPage *)*it, 0, 0);  
  6.   
  7.             bDestroyCurWndPage ? DestroyWndPage(pCurPage) : 0;  
  8.         }  
  9. </span> }  
  10.     __except(EXCEPTION_EXECUTE_HANDLER)  
  11.     {  
  12.         printf("\nWindowContainer::PopWndPage Catch Exception:%x!\n", GetExceptionCode());  
  13.     }  
  14.   
  15.     UnLockChilds();  
  16.   
  17.     return bResult;  
  18. }  

 

 

 

 

 

報錯內容以下:

 

1>.\WindowContainer.cpp(576) : error C2712: 沒法在要求對象展開的函數中使用 __try

 

解決方法:

 

 

 

 

 

錯誤消息

沒法在要求對象展開的函數中使用 __try

 

 

使用 /EHsc 時,帶有結構化異常處理的函數不能具備要求展開(毀壞)的對象。

 

可能的解決方案:

 

  • 將要求 SEH 的代碼移動到另外一個函數中。

  • 重寫使用 SEH 的函數以免使用具備析構函數的局部變量和參數。在構造函數或析構函數中不要使用 SEH。

  • 不使用 /EHsc 進行編譯。

 

示例

 

若是使用 /clr:pure 進行編譯並在 __try 塊中聲明指針到函數的靜態數組,則會發生 C2712 錯誤。靜態成員要求編譯器在/clr:pure 下使用動態初始化功能,這意味着 C++ 異常處理。可是,不容許在__try 塊中進行 C++ 異常處理。

 

下面的示例生成 C2712。

 

 

 

[cpp]  view plain
    1. // C2712.cpp  
    2. // compile with: /clr:pure /c  
    3. struct S1 {  
    4.    static int smf();  
    5.    void fnc();  
    6. };  
    7.   
    8. void S1::fnc() {  
    9.    __try {  
    10.       static int (*array_1[])() = {smf,};   // C2712  
    11.   
    12.       // OK  
    13.       static int (*array_2[2])();  
    14.       array_2[0] = smf;  
    15.     }  
    16.     __except(0) {}  
    17. }  
相關文章
相關標籤/搜索