在抛出期间构造对象时引发异常

Throwing an exception while constructing object during a throw?

本文关键字:对象 异常      更新时间:2023-10-16

我有一个异常类:

class MyException : public std::exception
{
public:
    MyException( char* message )
        : message_( message )
    {
        if( !message_ ) throw std::invalid_argument("message param must not be null");
    }
};

在我的投掷现场:

try {
    throw MyException( NULL );
}
catch( std::exception const& e ) {
    std::cout << e.what();
}

(代码未编译,因此请原谅任何错误(

我想知道当我在构造时由于另一次抛出而从构造函数中抛出时会发生什么。我认为这是合法的,并且捕获最终会捕获一个std::invalid_argument,并且之前抛出的异常(MyException(将被忽略或取消。

我使用此设计的目标是在我的异常类中强制实施不变量。 message_永远不应该是 NULL,而且我不想在我的 what() 重载中使用 if 条件来检查它是否为 null,所以我在构造函数中检查它们并在它们无效时抛出。

这是正确的,还是行为不同?

你打算抛出的对象(在本例中为 MyException (必须先成功构造,然后才能抛出。所以你还没有扔掉它,因为它还没有被构建出来。

所以这将起作用,从MyException的构造函数中抛出异常。不会触发"处理异常时引发异常导致std::terminate"问题。

15.1 抛出异常 n3376

第7段

如果异常处理机制在完成对要抛出的表达式的计算后但在捕获异常之前调用通过异常退出的函数,则调用 std::terminate (15.5.1(。

这意味着在构造函数(在本例中被抛出的对象(完成之前,不会发生任何特殊情况。但是在构造函数完成后,任何其他 uncought 异常都将导致调用terminate()

该标准继续提供了一个示例:

struct C
{
       C() { }
       C(const C&) { throw 0; }
};
int main()
{
  try
  {
    throw C();   // calls std::terminate()
  }
  catch(C) { }
}

这里调用终止是因为对象是首先创建的。但随后调用复制构造以将异常复制到保留位置 (15.1.4(。在此函数调用(复制构造(期间,将生成未捕获的异常,从而调用终止。

因此,如图所示的代码应按预期工作。

  • 任一项:生成一个带有良好消息的MyException并抛出
  • 或者:生成并抛出std::invalid_argument

如果要检查应始终为真的不变量,则应使用断言。异常适用于预期会发生的异常情况,例如极端情况或错误的用户输入。

如果使用异常来报告代码中的错误,则可能会意外地使用 catch(...( 隐藏它们。如果您正在编写库代码,这一点尤其重要,因为这样您永远不知道其他人是否会捕获并忽略异常,即使这意味着系统已达到无效状态。

断言的另一个优点是,如果需要,可以完全禁用它们,这样它们就不会再产生任何性能损失。这使您对调试版本中的这些检查非常偏执,并且仍然拥有闪电般的快速发布版本。