首页 文章

如何抛出C异常

提问于
浏览
163

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

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

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

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

5 回答

  • 246

    简单:

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

    只需在需要的地方添加 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_exception和std :: throw_with_nested

    它在StackOverflow herehere中进行了描述,如何在代码中使用 get a backtrace on your exceptions 而无需调试器或繁琐的日志记录,只需编写一个适当的异常处理程序即可重新抛出嵌套异常 .

    由于您可以对任何派生的异常类执行此操作,因此可以向此类回溯添加大量信息!你也可以看一下我的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"
    
  • 1

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

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

    在自定义异常的情况下,希望 ADD 到此处描述的其他答案附加说明 .

    如果您创建自己的自定义异常,该派生自 std::exception ,当您捕获"all possible"异常类型时,应始终使用可能被捕获的"most derived"异常类型启动 catch 子句 . 查看示例(要做什么 NOT ):

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

    NOTE:

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

    1)正如您所看到的,当您按原样运行程序时,将执行第一个catch子句(这可能是您首先想要做的事情) .

    2)即使第一个catch子句中捕获的类型是 std::exception 类型, what() 的"proper"版本也将被调用 - 因为它被引用捕获(至少将捕获的参数 std::exception 类型更改为值 - 您将体验到行动中的"object slicing"现象) .

    3)如果“由于XXX异常被抛出的事实导致的某些代码......”对异常类型做了重要的事情,那么这里的代码存在不当行为 .

    4)如果捕获的对象是"normal"对象,这也是相关的: class Base{};class Derived : public Base {} ...

    5) g++ 7.3.0 在Ubuntu 18.04.1上产生一个警告,表明上述问题:

    在函数'void illustrationsDerivedExceptionCatch()':item12Linux.cpp:48:2:警告:类型'MyException'的异常将被捕获catch(const MyException&e)^ ~~~~ item12Linux.cpp:43:2:警告:通过早期的'std :: exception'catch处理程序(const exception&e)^ ~~~~

    Again ,我会说,这个答案只是对这里描述的其他答案的答案(我认为这一点值得一提,但却无法在评论中描述) .

相关问题