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块重新抛出异常,在哪种情况下,下一个捕获块完成这项工作.
我仍然感到紧张,因为在一个或两个编译器中进行的测试不能证明标准所说的内容,并且因为我的经验表明我认为常识通常与语言所保证的不同.
那么 - 这种处理异常(使用引用类型捕获它们)的模式是否安全?或者我应该做些别的事,比如......
还行吧.通过常量引用捕获异常实际上很好(并且很难捕获指针).按值捕获会产生不必要的副本.编译器足够智能以正确处理异常(及其破坏) - 只是不要尝试在catch块之外使用异常引用;-)
实际上,我经常做的是从std :: runtime_error(继承自std :: exception)继承我的层次结构.然后我可以使用.what(),并在处理更多异常时使用更少的catch块.
这种模式绝对安全.
有一些特殊规则可以延长抛出对象的生命周期.实际上,只要它被处理它就存在,并且它保证存在直到catch处理它的最后一个块的末尾.
例如,一个非常常见的习惯用法是派生自定义异常std::exception,覆盖其what()成员函数,并通过引用捕获它,以便您可以使用一个catch子句从各种异常中打印错误消息.