我使用C++ catch子句,异常类的族和破坏是否合理?

Ste*_*314 7 c++ exception-handling

偶尔,我会注意到我已经使用多年的编码模式,这让我很紧张.我没有具体的问题,但我也不记得为什么我采用了这种模式,并且它的某些方面似乎与某些反模式匹配.这最近发生在WRT我的一些代码如何使用异常.

令人担忧的事情涉及我通过"引用"捕获异常的情况,以类似于我将参数处理为函数的方式处理它.这样做的一个原因是我可以拥有异常类的继承层次结构,并根据应用程序指定更通用或更精确的捕获类型.例如,我可能会定义......

class widget_error {};
class widget_error_all_wibbly : public widget_error {};
class widget_error_all_wobbly : public widget_error {};

void wibbly_widget ()
{
  throw widget_error_all_wibbly ();
}

void wobbly_widget ()
{
  throw widget_error_all_wobbly ();
}

void call_unknown_widget (void (*p_widget) ())
{
  try
  {
    p_widget ();
  }
  catch (const widget_error &p_exception)
  {
    //  Catches either widget_error_all_wibbly or
    //  widget_error_all_wobbly, or a plain widget_error if that
    //  is ever thrown by anything.
  }
}
Run Code Online (Sandbox Code Playgroud)

现在这让我很担心,因为我注意到在一个函数中构造了一个类实例(作为throw的一部分),但是在该函数退出之后被引用(通过p_Exception catch-clause"参数").这通常是一个反模式 - 一个局部变量的引用或指针或在函数内创建的临时变量,但在函数退出时传递出来,通常是一个悬空引用/指针,因为局部变量/ temporary被破坏并且内存被释放当函数退出时.

一些快速测试表明上面的抛出可能没问题 - 当函数退出时,throw子句中构造的实例不会被破坏,但是当处理它的catch子句完成时会被破坏 - 除非catch块重新抛出异常,在哪种情况下,下一个捕获块完成这项工作.

我仍然感到紧张,因为在一个或两个编译器中进行的测试不能证明标准所说的内容,并且因为我的经验表明我认为常识通常与语言所保证的不同.

那么 - 这种处理异常(使用引用类型捕获它们)的模式是否安全?或者我应该做些别的事,比如......

  • 捕获(并显式删除)指向堆分配实例的指针,而不是引用看起来(当抛出时)非常像临时的东西?
  • 使用智能指针类?
  • 使用"pass-by-value"catch子句,并接受我无法从具有一个catch子句的层次结构中捕获任何异常类?
  • 我没有想到的东西?

Tim*_*Tim 9

还行吧.通过常量引用捕获异常实际上很好(并且很难捕获指针).按值捕获会产生不必要的副本.编译器足够智能以正确处理异常(及其破坏) - 只是不要尝试在catch块之外使用异常引用;-)

实际上,我经常做的是从std :: runtime_error(继承自std :: exception)继承我的层次结构.然后我可以使用.what(),并在处理更多异常时使用更少的catch块.


Jam*_*lis 6

这种模式绝对安全.

有一些特殊规则可以延长抛出对象的生命周期.实际上,只要它被处理它就存在,并且它保证存在直到catch处理它的最后一个块的末尾.

例如,一个非常常见的习惯用法是派生自定义异常std::exception,覆盖其what()成员函数,并通过引用捕获它,以便您可以使用一个catch子句从各种异常中打印错误消息.