提问人:einpoklum 提问时间:11/29/2022 最后编辑:einpoklum 更新时间:11/29/2022 访问量:196
我应该使用 'std::uncaught_exceptions()' 来决定是否从我的 dtor 抛出异常吗?[已结束]
Should I use `std::uncaught_exceptions()` to decide whether to throw an exception from my dtor? [closed]
问:
我有一个类,其 ctor 进行驱动程序调用,其 dtor 进行匹配的终止/释放驱动程序调用。这些调用可能会失败。问题自然出在dtor上。
我自然知道避免 dtor 中异常的常识,因为如果你在堆栈展开期间抛出一个,你会得到 .但是 - 如果可以的话,我宁愿不要只是“吞下”这些错误而不报告它们。那么,编写代码是否合法/惯用:std::terminate
~MyClass() noexcept(false) {
auto result = something_which_may_fail_but_wont_throw();
if (std::uncaught_exceptions() == 0) {
throw some_exception(result);
}
}
或者这只是巴洛克风格,不是一个好主意?
注意:此类无权访问标准输出/错误流,也无法访问日志等。
答:
如果您唯一要做的就是检查是否为零,那么您可能会错过一些可以安全传播异常的情况。例如,考虑uncaught_exceptions()
struct X {
~X() noexcept(false) {
if (std::uncaught_exceptions() == 0) throw FooException{};
}
};
struct Y {
~Y() {
try {
X x;
} catch (const FooException&) {
// handle exception
}
}
};
int main() {
try {
Y y;
throw BarException{};
} catch (const BarException&) {
// handle
}
}
在这里,将在堆栈展开过程中被破坏。在析构函数期间,飞行中有一个未捕获的异常。析构函数创建一个对象,其析构函数随后必须决定是否抛出 .这样做是安全的,因为在它到达将被调用的点之前,将有机会抓住它。但确定未捕获的异常正在运行中,因此它决定不引发异常。y
Y
X
FooException
FooException
std::terminate
X::~X
这在技术上没有任何问题,但 try-catch 块的行为取决于调用的上下文,这可能会令人困惑。理想情况下,在这种情况下仍应引发异常。Y::~Y
Y::~Y
X::~X
N4152 解释了正确的使用方法:std::uncaught_exceptions
想要知道是否正在运行其析构函数以展开此对象的类型可以在其构造函数中查询并存储结果,然后在其析构函数中再次查询;如果结果不同,则此析构函数将作为堆栈展开的一部分被调用,因为该异常在对象构造之后引发。
uncaught_exceptions
uncaught_exceptions
在上面的例子中,需要存储其构造期间的值,即 1。然后,析构函数将看到该值仍为 1,这意味着让异常从析构函数中逃脱是安全的。X::X()
std::uncaught_exceptions()
这种技术只应该用于你确实需要从析构函数抛出的情况,并且你没有接受这样一个事实,即如果检查失败,从析构函数抛出的任何目的都将无法实现(迫使析构函数吞下错误条件或终止程序)。这种情况很少见。std::uncaught_exceptions()
评论
uncaught_exceptions
在任何情况下都不应该从 c++ 析构函数中抛出。
从 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
18.5.1 std::terminate() 函数 [except.terminate] ...(1.4) — 当堆栈展开过程中对象被破坏时 (18.2) 通过引发异常而终止,或者
因此,不投入破坏者不仅仅是“智慧”。这将导致您的程序崩溃/退出。
相反,我为这种情况所做的是,有一个类的方法,称为“Complete”。
在“完成”方法中,您可以检查错误代码并安全抛出。
可以添加初始化为 false 的数据成员 (completed) - private - 并在 Complete () 方法中设置 true。在析构函数中,断言其为真(这样您就可以捕获忘记调用 Complete 的任何情况)。
从析构函数中投掷可能会导致程序严重混乱。
评论
std::uncaught_exceptions()
std::terminate()
complete()
下一个:堆垛放卷背后的机制是什么?
评论