【问题标题】:How to throw a C++ exception如何抛出 C++ 异常
【发布时间】:2012-01-18 19:41:12
【问题描述】:

我对异常处理(即如何为自己的目的自定义 throw、try、catch 语句)的理解非常差。

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

我希望该函数在 a 或 b 为负数时抛出带有某些消息的异常。

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

【问题讨论】:

  • 你应该阅读这个:gotw.ca/publications/mill22.htm.
  • @OliCharlesworth,你不觉得这对那些对基础知识感到困惑的人来说有点过分吗?
  • 多余的异常值得避免。如果您不希望您的调用者传递负值,则通过将 unsigned int 指定为函数签名中的参数来使其更加明显。再说一次,我认为你应该只为真正异常的事情抛出和捕获异常。
  • @Mark:我最初误解了这个问题是关于是否应该在函数上使用throw() 异常规范。
  • 更通俗点,有this,表示可以throw 5;throw "Bad!";catch (...) { }等...

标签: c++ exception-handling


【解决方案1】:

简单:

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

【讨论】:

  • 而且您应该始终将异常捕获为 const
  • @TerryLiYifeng 如果自定义异常更有意义,那就去吧。您可能仍希望从 std::exception 派生并保持接口相同。
  • +1'ed 再次,但我认为 const 它非常重要 - 因为它突出了它现在是一个临时对象的事实 - 所以修改是无用的。
  • @AdrianCornish:不过这并不是暂时的。非常量捕获 can be useful.
  • 您通常会使用简单的throw; 重新抛出(重新抛出原始对象并保留其类型)而不是throw e;(抛出捕获对象的副本,可能会更改其类型)。
【解决方案2】:

虽然这个问题已经很老了并且已经得到解答,但我只想添加一个关于如何在 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】:

    只需在需要的地方添加throw,并将try 块添加到处理错误的调用者。按照惯例,您应该只抛出源自std::exception 的东西,因此首先包括&lt;stdexcept&gt;

    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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        添加到这个答案,因为此时为此问答创建另一个答案似乎没有好处。

        如果您创建自己的自定义异常(派生自 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;
        }
        

        注意:

        1. 正确的顺序应该是反之亦然,即 - 首先是 catch (const MyException&amp; e),然后是 catch (const std::exception&amp; e)

        2. 如您所见,当您按原样运行程序时,将执行第一个 catch 子句(这可能是您最初想要的)。

        3. 即使在第一个 catch 子句中捕获的类型是 std::exception 类型,what() 的“正确”版本也会被调用 - 因为它是通过引用捕获的(至少更改被捕获的参数 std::exception 类型以价值为导向 - 您将体验到实际的“对象切片”现象)。

        4. 如果“由于抛出 XXX 异常而导致的某些代码......”对异常类型有重要的影响,那么您的代码在此处存在不当行为。

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

        6. g++ 7.3.0 在 Ubuntu 18.04.1 上会产生一个警告,指出上述问题:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-22
          • 2017-07-01
          • 2015-08-04
          • 1970-01-01
          相关资源
          最近更新 更多