c++11 異常處理


概要

異常是程序執行期產生問題,比如嘗試除以零的操作。

異常提供了一種轉移程序控制權的方式。C++ 異常處理涉及到三個關鍵字:trycatchthrow

  • throw: 當問題出現,程序通過throw拋出一個異常。
  • catch: 在你想要處理問題的地方,通過異常處理程序捕獲異常。
  • try: try 塊中的代碼標識將被激活的特定異常。它后面允許跟着一個或多個 catch 塊。

使用 try/catch 語句的語法:

try { // 保護代碼 }

catch( ExceptionName e1 ) {

// catch 塊

}

catch( ExceptionName e2 ) {

// catch 塊

}

catch( ExceptionName eN ) {

// catch 塊

}

如果 try 塊在不同場景拋出不同異常,此時可嘗試羅列多個 catch 語句,用於捕獲不同類型異常。

拋出異常

 

使用 throw 語句在代碼塊中任何位置拋出異常。throw 語句的操作數可以是任意表達式,表達式的結果類型決定了拋出異常的類型。

示例:

double division(int a, int b) {

if( b == 0 ) {

   throw "Division by zero condition!"; } return (a/b);

}

捕獲異常

 

catch 塊跟在 try 塊后面,用於捕獲異常。可指定想要捕捉的異常類型,由 catch 關鍵字后括號內的異常聲明類型決定。

try {

// 保護代碼 }

catch( ExceptionName e ) {

// 處理 ExceptionName 異常的代碼

}

上面的代碼會捕獲一個類型為 ExceptionName 的異常。如果您想讓 catch 塊能夠處理 try 塊拋出的任何類型的異常,則必須在異常聲明的括號內使用省略號 ...,如下所示:

try { // 保護代碼 }

 catch(...) { // 能處理任何異常的代碼

}

下面是一個實例,拋出一個除以零的異常,並在 catch 塊中捕獲該異常。

實例

#include <iostream>

using namespace std;

double division(int a, int b) {

 if( b == 0 ) {

  throw "Division by zero condition!"; }

  return (a/b);

}

int main () {

 int x = 50; int y = 0; double z = 0;

 try {

 z = division(x, y); cout << z << endl;

 }

 catch (const char* msg) {

  cerr << msg << endl;

 }  

 return 0;

}

此代碼拋出了一個類型為 const char* 的異常,因此捕獲異常時,須在 catch 塊中使用 const char*。以上代碼被編譯和執行時,結果如下:

Division by zero condition!

C++ 標准異常

C++ 提供一系列標准異常,定義在 <exception> 中,我們可以在程序中使用這些標准的異常。它們是以父子類層次結構組織起來的,如下所示:

std:exception

std::bad_alloc

 

std::bad_cast

 

std::bad_typeid

 

std::bad_exception

 

std::logic_error

std::domian_error

std::invalid_argument

std::length_error

std::out_of_range

std::runtime_error

std::overflow_error

std::range_error

std::underflow_error

std::exception       該異常是所有標准 C++ 異常的父類。

std::bad_alloc       該異常可以通過 new 拋出。

std::bad_cast 該異常可以通過 dynamic_cast 拋出。

std::bad_exception      這在處理 C++ 程序中無法預期的異常時非常有用。

std::bad_typeid     該異常可以通過 typeid 拋出。

std::logic_error      理論上可以通過讀取代碼來檢測到的異常。

std::domain_error  當使用了一個無效的數學域時,會拋出該異常。

std::invalid_argument   當使用了無效的參數時,會拋出該異常。

std::length_error    當創建了太長的 std::string 時,會拋出該異常。

std::out_of_range  該異常可以通過方法拋出,例如 std::vector 和 std::bitset<>::operator[]()。

std::runtime_error  理論上不可以通過讀取代碼來檢測到的異常。

std::overflow_error       當發生數學上溢時,會拋出該異常。

std::range_error     當嘗試存儲超出范圍的值時,會拋出該異常。

std::underflow_error     當發生數學下溢時,會拋出該異常。

自定義異常

通過繼承和重載 exception 類來定義新的異常。示例:

#include <iostream>

#include <exception>

using namespace std;

struct MyException : public exception {

  const char * what () const throw () {

  return "C++ Exception";

 }

};

int main() {

try { throw MyException(); }

catch(MyException& e) {

 std::cout << "MyException caught" << std::endl; std::cout << e.what() << std::endl; }

 catch(std::exception& e) {

 //其他的錯誤 }

}

這將產生以下結果:

MyException caught

C++ Exception

注解:

  1. what() 是異常類提供的一個公共方法,它已被所有子異常類重載。這將返回異常產生的原因。
  2. C++函數后面加關鍵字throw(something)限制,是對這個函數的異常安全性作出限制(注:c++11使用noexcept)。

示例:

void fun() throw() 表示fun不允許拋出任何異常,即fun是異常安全的。

void fun() throw(...) 表示fun可以拋出任何形式的異常。

void  fun() throw(exceptionType) 表示fun只能拋出exceptionType類型的異常。

noexcept關鍵字

(c++11)

動態異常聲明thro由於很少使用,在c++11中被棄用, 表示函數不會拋出異常的動態異常聲明throw()也被新的noexcept異常聲明取代, noexcept修飾的函數不會拋出異常, 在c++11中,如果noexcept修飾的函數拋出了異常,編譯器可以選擇直接調用std::terminate()

來終止程序的運行,這比基於異常機制的throw()在效率上會高出一些。使用noexcept可有效阻止異常的傳播與擴散

#include <iostream>

using namespace std;

void throw_(){ throw 1; }

void NoBlockThrow(){ throw_(); }

void BlockThrow() noexcept { throw_(); }

int main()

{

       /*try {

              throw_();

       }

       catch (...)       {

              cout << "found throw." << endl;

       }

       try   {

              NoBlockThrow();

       }

       catch (...){

              cout << "throw is not blocked" << endl;

       }*/

       try{

              BlockThrow();

       }

       catch (...){

              cout << "found throw 1" << endl;

       }

       //noexcept修飾的函數拋出異常,編譯器直接調用std::terminate()終止程序運行,

}

這個程序運行時,前兩個try catch會正常輸出xx到控制台,最后一個try-catch塊,程序會彈出錯誤框表示程序已被終止,這是因為如果noexcept修飾的函數拋出了異常,那么編譯器就直接調用std::terminate()終止了程序的運行。

異常嵌套處理

當處理第1個異常時,可能會觸發第2種異常情況,從而要求拋出第2個異常,但問題是當拋出第2個異常時,正在處理的第1個異常信息會丟失。C++用嵌套異常(nested exception)的概念提供了解決這一問題的方案,嵌套異常允許將捕獲的異常嵌套到新的異常環境。使用std::throw_nested()可以拋出嵌套了異常的異常。第2個異常的catch處理程序可以使用dynamic_cast訪問代表第一個異常的nested_exception。

下面的示例演示了嵌套異常的用法。

示例定義了一個從exception派生的MyExcepion類,其構造函數接受一個字符串。

#pragma once

#include<exception>

#include<iostream>

#include<string>

using namespace std;

class MyException :public exception

{

public:

         MyException(const char* msg) :mMsg(msg) {}

         virtual ~MyException() noexcept {}

         virtual const char* what() const noexcept override {

                   return mMsg.c_str();

         }

private:

         string mMsg;

};

    當處理第一個異常,且需要拋出嵌套了第一個異常的第二個異常時,需要使用std::throw_with_nested()函數。下面的doSomething()函數排除一個runtime_error異常,這個異常立即被處理程序捕獲。捕獲處理程序編寫了一條消息,然后使用throw_with_nested()函數拋出第二個異常,第一個異常嵌套在其中。注意嵌套異常時自動實現的。

void doSomething()

{

         try {

                   throw runtime_error("Throwing a runtime_error exception");

         }

         catch (const runtime_error&e) {

                   cout << __func__ << " caught a runtime_error" << std::endl;

                   cout << __func__ << " throwing MyException" << endl;

                   throw_with_nested(MyException("MyException with nested runtime_error)"));

         }

下面的main()函數演示如何處理嵌套異常。該段代碼調用了doSomething()函數,還有一個處理MyException類型異常的處理程序,當捕獲到這類異常時,會編寫一條消息,然后使用dynamic_cast訪問嵌套的異常。如果內部沒有嵌套異常,結果為空指針。如果存在嵌套異常,調用nest_exception的rethrow_nested()方法。這樣會再次拋出嵌套的異常,這一異常可以在另一個try/catch塊中捕獲。

int main()

{

         try {

                   doSomething();

         }

         catch (const MyException&e)

         {

                   cout << __func__ << " caught MyException: " << e.what() << endl;

                   const nested_exception *pNested = dynamic_cast<const nested_exception*>(&e);

                   if (pNested)      {

                            try     {

                                     pNested->rethrow_nested();

                            }

                            catch (const std::runtime_error& e)    {

                                     //handle nested exception

                                     cout << " Nested exception: " << e.what() << endl;

                            }

                   }

         }

         return 0;

}

輸出結果:

doSomething caught a runtime_error

doSomthing throwing My Exception

main caught MyException: MyException with nested runtime_error>

Nested exception: Throwing a runtime_error exception


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM