3

我有一个异常类:

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

(代码未编译,如有错误请见谅)

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

这种设计的目标是在我的异常类中强制执行不变量。message_永远不应该为 NULL,我不想在我的what()重载中使用 if 条件来检查它是否为 null,所以我在构造函数中检查它们,如果它们无效则抛出。

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

4

3 回答 3

4

您打算抛出的对象(在这种情况下MyException)必须在它可以被抛出之前成功构造。所以你还没有扔掉它,因为它还没有被构建。

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

于 2012-05-03T18:07:35.980 回答
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)。在此函数调用(复制构造)期间,会生成未捕获的异常并因此调用终止。

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

  • 要么: AMyException生成一个好的消息并抛出
  • 或者:std::invalid_argument生成并抛出A
于 2012-05-03T18:24:37.563 回答
0

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

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

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

于 2012-05-03T18:44:51.637 回答