如何抛出C ++异常

时间:2011-12-12 20:51:05

标签: c++ exception-handling

我对异常处理的理解很差(即,如何根据自己的目的自定义throw,try,catch语句)。

例如,我已经定义了一个函数,如下所示:int compare(int a, int b){...}

当a或b为负数时,我希望函数能够抛出一些带有异常的消息。

我应该如何在函数的定义中解决这个问题?

5 个答案:

答案 0 :(得分:284)

简单:

#include <stdexcept>

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

标准库附带了一个很好的built-in exception objects集合。请记住,您应该始终按价值投掷并通过引用捕获:

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( ... ) { };

答案 1 :(得分:16)

只需在需要的地方添加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

答案 2 :(得分:12)

虽然这个问题相当陈旧且已经得到解答,但我只是想补充说明如何在C ++ 11中进行适当的异常处理:

使用std::nested_exceptionstd::throw_with_nested

StackOverflow herehere中描述了如何在代码中获取异常的回溯,而无需调试器或繁琐的日志记录,只需简单编写一个适当的异常处理程序,它将重新抛出嵌套的异常。

由于您可以对任何派生的异常类执行此操作,因此可以向此类回溯添加大量信息! 您还可以查看我的MWE on GitHub,其中回溯看起来像这样:

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"

答案 3 :(得分:6)

您可以定义在发生特定错误时要抛出的消息:

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; }

答案 4 :(得分:3)

自定义例外的情况下,想添加此处所述的其他答案。

在创建自己的自定义异常(源自std::exception的情况下),当捕获“所有可能的”异常类型时,应始终以“最衍生的”开头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;
}

注意:

0)正确的顺序应该相反,即-首先是catch (const MyException& e),然后是catch (const std::exception& e)

1)如您所见,当按原样运行程序时,将执行第一个catch子句(这可能是您首先要执行的操作)。

2)即使第一个catch子句中捕获的类型为std::exception类型,也将调用what()的“适当”版本-因为它被引用捕获(至少更改了捕获的参数std::exception的类型将按值表示-您将在操作中遇到“对象切片”现象。

3)如果“由于抛出XXX异常而导致了某些代码...”对异常类型具有重要意义,则这里的代码行为不正确。

4)如果捕获的对象是“正常”对象,例如class Base{};class Derived : public Base {} ...

,这也很重要。

5)在Ubuntu 18.04.1上的g++ 7.3.0会产生一条警告,指出上述问题:

  

在函数“ voidillustratedDerivedExceptionCatch()”中:   item12Linux.cpp:48:2:警告:将捕获 “ MyException”类型的异常     catch(const MyException&e)     ^ ~~~~

     

item12Linux.cpp:43:2:警告: 由较早的处理程序针对“ std :: exception”     捕获(常量异常&e)     ^ ~~~~

再次,我会说,这个答案只是对此处描述的其他答案添加(我认为这一点值得一提,但无法描述在评论中)。