簡體   English   中英

如何拋出 C++ 異常

[英]How to throw a C++ exception

我對異常處理的理解很差(即,如何為自己的目的自定義 throw、try、catch 語句)。

例如,我定義了一個函數如下: int compare(int a, int b){...}

我希望該函數在 a 或 b 為負時拋出帶有某些消息的異常。

我應該如何在函數的定義中解決這個問題?

簡單的:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

標准庫附帶了一個很好的內置異常對象集合,您可以拋出這些異常對象 請記住,您應該始終按值拋出並按引用捕獲:

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

您可以在每次嘗試后使用多個 catch() 語句,因此您可以根據需要分別處理不同的異常類型。

您還可以重新拋出異常:

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

無論類型如何,都可以捕獲異常:

catch( ... ) { };

雖然這個問題已經很老了並且已經得到了回答,但我只想添加一個關於如何在 C++11 中進行正確異常處理的注釋:

使用std::nested_exceptionstd::throw_with_nested

此處此處的StackOverflow 上進行描述,您可以通過簡單地編寫一個適當的異常處理程序來重新拋出嵌套異常,從而在不需要調試器或繁瑣的日志記錄的情況下獲得代碼中異常的回溯

由於您可以使用任何派生的異常類來執行此操作,因此您可以向此類回溯添加大量信息! 您也可以在 GitHub 上查看我的MWE ,其中的回溯如下所示:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

只需在需要的地方添加throw ,並將try塊添加到處理錯誤的調用者。 按照慣例,你應該只拋出從std::exception派生的東西,所以首先包括<stdexcept>

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

另外,看看Boost.Exception

您可以定義在發生特定錯誤時拋出的消息:

throw std::invalid_argument( "received negative value" );

或者你可以這樣定義它:

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

通常,你會有一個try ... catch塊,如下所示:

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }

添加到此答案中,因為此時為此問答創建另一個答案似乎並不有利。

在您創建自己的自定義異常的情況下,該異常派生自std::exception ,當您捕獲“所有可能的”異常類型時,您應該始終以可能catch的“派生最多的”異常類型開始catch子句。 見(中不要做什么)的例子:

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException::what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

筆記:

  1. 正確的順序應該是反之亦然,即 - 首先你catch (const MyException& e)然后是catch (const std::exception& e)

  2. 如您所見,當您按原樣運行程序時,將執行第一個 catch 子句(這可能是您一開始並不想要的)。

  3. 即使在第一個 catch 子句中捕獲的類型是std::exception類型,也會調用what()的“正確”版本 - 因為它是通過引用捕獲的(至少將捕獲的參數std::exception類型更改為按價值計算 - 您將在操作中體驗“對象切片”現象)。

  4. 如果“由於拋出 XXX 異常這一事實而產生的某些代碼......”對異常類型做了重要的事情,那么這里的代碼存在不當行為。

  5. 如果捕獲的對象是“普通”對象,例如: class Base{}; class Derived : public Base {} ...

  6. Ubuntu 18.04.1 上的g++ 7.3.0會產生一個警告,指出上述問題:

在函數 'voidIllustrationDerivedExceptionCatch()': item12Linux.cpp:48:2: 警告: 'MyException' 類型的異常將被捕獲​​ catch(const MyException& e) ^~~~~

item12Linux.cpp:43:2: 警告:由較早的處理程序用於 'std::exception' catch (const exception& e) ^~~~~

再次,我會說,這個答案只是添加到這里描述的其他答案(我認為這一點值得一提,但無法在評論中描述)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM