在投掷期间构造对象时抛出异常?

时间:2012-05-03 18:05:18

标签: c++ exception

我有一个异常类:

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,并且我不希望if条件检查我的what()重载中是否为null,所以我在构造函数中检查它们并抛出它们是否无效。< / p>

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

3 个答案:

答案 0 :(得分:4)

您想要抛出的对象(在本例中为MyException)必须在之前成功构造,然后才能抛出它。所以你还没有丢掉它,因为它尚未构建。

因此,这将起作用,从MyException的构造函数中抛出异常。您不会触发“在处理异常导致std::terminate”问题时抛出异常。

答案 1 :(得分:3)

15.1抛出异常n3376

第7段

  

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

这意味着直到构造函数(在这种情况下抛出的对象)完成后才会发生特殊情况。但是在构造函数完成任何其他未操作的异常后,将导致调用terminate()

标准继续提供一个例子:

struct C
{
       C() { }
       C(const C&) { throw 0; }
};

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

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

因此,您显示的代码应该按预期工作。

  • 要么:MyException生成一条好消息并抛出
  • 或者:生成std::invalid_argument并抛出

答案 2 :(得分:0)

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

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

断言的另一个优点是,如果您愿意,可以完全禁用它们,这样它们就不会再受到任何性能损失。这使您在调试版本中对这些检查非常偏执,并且仍然具有快速释放闪存。