如何抛出 C++ 异常
Posted
技术标签:
【中文标题】如何抛出 C++ 异常【英文标题】:How to throw a C++ exception 【发布时间】: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 (...)
等...
【参考方案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_exception
和std::throw_with_nested
它在 *** here 和 here 上进行了描述,您可以在代码中获取异常的回溯,而无需调试器或繁琐的日志记录,只需编写适当的异常将重新抛出嵌套异常的处理程序。
由于您可以对任何派生的异常类执行此操作,因此您可以向此类回溯添加大量信息! 你也可以看看我的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
的东西,因此首先包括<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。
【讨论】:
【参考方案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;
注意:
正确的顺序应该是反之亦然,即 - 首先是 catch (const MyException& e)
,然后是 catch (const std::exception& e)
。
如您所见,当您按原样运行程序时,将执行第一个 catch 子句(这可能是您最初不想要的)。
即使在第一个 catch 子句中捕获的类型是 std::exception
类型,what()
的“正确”版本也会被调用 - 因为它是通过引用捕获的(至少更改被捕获的参数 std::exception
类型以价值为导向 - 您将体验到实际的“对象切片”现象)。
如果“由于抛出 XXX 异常而导致的某些代码......”对异常类型有重要的影响,那么您的代码在此处存在不当行为。
如果捕获的对象是“正常”对象,例如:class Base;
和 class Derived : public Base
...
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) ^~~~~
再次,我会说,这个答案只是添加到这里描述的其他答案(我认为这一点值得一提,但无法描述它在评论中)。
【讨论】:
以上是关于如何抛出 C++ 异常的主要内容,如果未能解决你的问题,请参考以下文章