gpt4 book ai didi

c++ - 如果没有 uncaught_exception,C++ 析构函数中的异常

转载 作者:可可西里 更新时间:2023-11-01 17:35:30 25 4
gpt4 key购买 nike

人们强烈反对从析构函数中抛出异常。取this answer举个例子。我想知道是否std::uncaught_exception()可用于可移植地检测我们是否由于某些其他异常而处于展开堆栈的过程中。

我发现自己故意在析构函数中抛出异常。提及两个可能的用例:

  • 一些涉及刷新缓冲区的资源清理,因此失败可能意味着输出被截断。
  • 销毁持有 std::exception_ptr 的对象,该对象可能包含在不同线程中遇到的异常。

简单地忽略这些异常情况感觉是完全错误的。并且有可能通过抛出异常,一些异常处理程序可能能够提供比析构函数本身写入 std::cerr 更有用的上下文信息。此外,为所有失败的断言抛出异常是我的单元测试方法的重要组成部分。在这种情况下,错误消息后跟被忽略的错误条件不起作用。

所以我的问题是,抛出异常是否可以,除非正在处理另一个异常,或者是否有理由不这样做?

将其写入代码:

Foo::~Foo() {
bool success = trySomeCleanupOperation();
if (!success) {
if (std::uncaught_exception())
std::cerr << "Error in destructor: " << errorCode << std::endl;
else
throw FooOperationFailed("Error in destructor", errorCode);
}
}

据我所知,这应该是安全的,并且在许多情况下比根本不抛出异常要好。但我想验证这一点。

最佳答案

Herb Sutter 撰写了有关该主题的文章:http://www.gotw.ca/gotw/047.htm

他的结论是永远不要从析构函数中抛出,始终使用在无法抛出的情况下使用的机制来报告错误。

两个原因是:

  • 它并不总是有效。有时 uncaught_exception 返回 true 但抛出它是安全的。
  • 以两种不同的方式报告相同的错误是糟糕的设计,如果用户想知道错误,则必须考虑这两种方式。

请注意,对于任何给定的可重用代码片段,无法确定它在堆栈展开期间永远不会被调用。无论您的代码做什么,您都不能确定它的某些用户不想从带有 try/catch 的析构函数调用它来处理它的异常。因此,您不能依赖 uncaught_exception 如果可以安全抛出则总是返回 true,除非通过记录函数,“不得从析构函数中调用”。如果你诉诸于此,那么所有调用者还必须记录他们的功能,“不得从析构函数中调用”并且你有一个更烦人的限制。

除此之外,nothrow 保证对用户很有值(value) - 如果他们知道他们所做的特定事情不会抛出,它可以帮助他们编写异常安全的代码。

一种解决方法是为您的类提供一个成员函数close,它调用trySomeCleanupOperation 并在失败时抛出。然后析构函数调用 trySomeCleanupOperation 并记录或抑制错误,但不抛出错误。然后用户如果想知道自己的操作是否成功可以调用close,如果不关心就让析构函数去处理(包括析构函数作为stack的一部分被调用的情况)展开,因为在用户调用 close 之前抛出异常)。 “啊哈!”,你说,“但这违背了 RAII 的目的,因为用户必须记住调用 close!”。是的,有一点,但问题不在于 RAII 是否可以做你想做的一切。它不能。有问题的是它一贯做的比你想要的少(你希望它在 trySomeCleanupOperator 失败时抛出异常),还是令人惊讶地做的少 在堆栈展开期间使用时。

Furthermore, throwing exceptions for all failed assertions is an important part of my unit testing approach

这可能是一个错误 - 您的单元测试框架应该能够将 terminate() 视为测试失败。假设断言在堆栈展开期间失败——您当然想记录它,但您不能通过抛出异常来做到这一点,所以您已经把自己逼到了一个角落。如果您的断言终止,那么您可以将它们检测为终止。

不幸的是,如果您终止,那么您将无法运行其余的测试(至少,不能在该过程中运行)。但是,如果断言失败,那么一般来说您的程序处于未知且可能不安全的状态。因此,一旦断言失败,您就不能依赖在该过程中做任何其他事情。您可以考虑将您的测试框架设计为使用多个进程,或者只是接受足够严重的测试失败将阻止其余测试运行的事实。在测试框架外部,您可以认为您的测试运行具有三种可能的结果“全部通过、部分失败、测试崩溃”。如果测试运行未能完成,则您不会将其视为通过。

关于c++ - 如果没有 uncaught_exception,C++ 析构函数中的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15222286/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com