如何引发C ++异常


259

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

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

我希望函数在a或b为负数时引发一些消息异常。

在函数的定义中应该如何处理?


3
您应该阅读以下内容:gotw.ca/publications/mill22.htm
奥利弗·查尔斯沃思

37
@OliCharlesworth,您认为对基本知识感到困惑的人难道不值得吗?
马克·兰瑟姆

6
多余的异常值得避免。如果您不希望调用者传递负值,则可以通过unsigned int在函数签名中指定作为参数来使其更明显。再说一遍,我属于学校,您应该只对确实异常的事物抛出异常并加以捕获。
AJG85 2011年

1
@Mark:我最初误解了有关是否应该throw()在函数上使用异常规范的问题。
奥利弗·查尔斯沃思

Answers:


363

简单:

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

26
你应该总是捕捉异常为const
阿德里安康沃尔

2
@TerryLiYifeng如果自定义异常更有意义,那就去吧。您可能仍想从std :: exception派生并保持接口相同。
nsanders 2011年

2
再次+1,但我认为const非常重要-因为它突出显示了它现在是临时对象这一事实-因此修改是无用的。
Adrian Cornish

2
@AdrianCornish:但这并不是真的临时的。非常量捕获可能很有用
GManNickG 2011年

26
通常,您可以使用简单的方法throw;(抛出原始对象并保留其类型)而不是throw e;(抛出所捕获对象的副本,可能会更改其类型)。
Mike Seymour

17

只需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


15

尽管这个问题已经很老了,并且已经被回答了,但我只想添加一条注释,说明如何在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"

8

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

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

6

想要在自定义例外的情况下,将此处所述的其他答案添加到其他注释中。

在创建自己的自定义异常(源自)的情况下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子句中捕获的类型是type std::exceptionwhat()也将调用的“适当”版本-因为它是通过引用捕获的(至少将catch的参数std::exception类型更改为按值捕获-您将体验到活动中的“对象切片”现象。

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

4)如果捕获的对象是“普通”对象,例如:class Base{};class Derived : public Base {}...,这也很重要。

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

在函数'voidillustratDerivedExceptionCatch()'中:item12Linux.cpp:48:2:警告:类型为'MyException'的异常将被捕获​​catch(const MyException&e)^ ~~~~

item12Linux.cpp:43:2:警告: 由早期处理程序处理'std :: exception'catch (const exception&e)^ ~~~~

同样,我会说,这个答案只有到ADD到这里所描述的其他答案(我认为这一点值得一提,但在注释不能描绘它)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.