c++ 如果例外状况程式码路径掷回另一个例外状况,会发生什麽情况?

sdnqo3pr  于 2022-12-01  发布在  其他
关注(0)|答案(2)|浏览(148)

我今天在工作中遇到了一些代码,其中析构函数抛出了异常。

  • 你永远不应该从析构函数中抛出异常。我指出这一点是因为我确信如果我不抛出异常,其他人也会抛出异常。

我被告知这是一个明智的设计决策,需要在遇到另一个故障的情况下清理一些数据。正在利用堆栈展开的过程来进行清理。在正常情况下,清理过程是成功的,不会抛出异常。但今天我遇到了一个清理失败并抛出异常的情况,因此我开始调查。

  • 除上述内容外,由于这不是一个关于我所工作的组织使用的代码审查代码的问题,因此我的问题如下。*
如果引发异常后的代码路径引发了另一个异常,会发生什么情况?

由于这是一个不寻常的情况,我只知道2种方式,这可能发生。
1.第一种情况是抛出一个异常,它被catch块捕获,然后抛出。这与抛出一个未被捕获的异常是一样的。关于这一点已经有一些问题,例如here。简而言之,调用了terminate()
1.当抛出一个异常时,Stack Unwinding process就开始了。这个过程调用栈分配对象的析构函数。因此,我所知道的引发嵌套异常的唯一其他方法是在析构函数内部抛出,就像我今天遇到的一样。
我想不出任何进一步的可能性。如果有任何可能性的话,我很有兴趣听听。
关于第二点,在这种情况下会发生什么?

oprakyz7

oprakyz71#

如果引发异常后所遵循的代码路径引发了另一个异常,会发生什么情况?
这取决于第二个异常触发的确切时间和地点。在C++中,你可以有“无限”数量的当前抛出的未捕获异常,并且一切都必须正常工作。这使得实现非常复杂。
1.这是不正确的,catch块可以在不调用terminate的情况下引发异常,甚至可以通过throw;显式支持。

  • 取决于你所说的“嵌套”,我知道还有两种情况会导致std::terminate,但“嵌套”异常是支持的,如下所述。
  • 引发的表达式中的异常对象的构造函数引发。
  • 复制catch区块中的建构函式(如果由值撷取)掷回。

一般来说,异常处理是:

  1. throw语句,则从表达式创建异常对象E并将其存储在 * 某处 *,比如说“可能不在堆栈上”。
    1.找到与异常处理程序匹配的catch块处理程序,并将控制转移到它。如果这样的处理程序不存在,则调用std::terminate
    1.在控制转移期间,相关局部对象以其构造的相反顺序被适当地破坏。
  2. catch块的参数是从E构造的,异常现在被认为是捕获的,并且catch块开始执行。块内的任何throw都是新的异常,并且进程重新开始。
    标准没有指定2、3的顺序。编译器可以首先搜索处理程序,如果没有找到,则立即终止程序,在这种情况下不保证发生栈展开。AFAIK gcc,clang现在不展开栈。
    栈展开调用本地对象的析构函数,而本地对象的析构函数可以调用更多的函数。这些函数可以抛出,导致更多的栈展开和更多的析构函数调用,这又会导致更多的异常......这是一种非常有效的方法,可以让任意数量的挂起的、尚未处理的异常同时处于活动状态。实际上,你可以通过调用std​::​uncaught_­exceptions来查询该数量
    唯一的问题是当抛出的未捕获异常逃逸到前一个(仍然)未捕获的挂起异常的堆栈展开过程中时--也就是当它从析构函数抛出时,**直接被堆栈展开算法调用,**因为在里面没有找到任何匹配的处理程序。这种情况导致std::terminate调用,因为没有其他合理的方法可以做。
    支持从析构函数本身引发,并且可以捕获,但由于您不知道触发该调用的确切原因,因此实际上强烈建议不要这样做
if (std::uncaught_exceptions()==0)// Safe throw from dtor.
    throw 42;

会起作用的,但请假装你没有读过。一个人也应该问问自己,如果一个物体拒绝被摧毁,这意味着什么。

aiazj4mn

aiazj4mn2#

我在上面提出问题的方式使答案看起来,也许,并不那么令人惊讶。
特别是,当我开始研究这一点时,我没有考虑未捕获异常的情况,如上面的第1点。
考虑到这一点,两种情况下的结果相同可能就不足为奇了。调用terminate()
这里有一个Godbolt demonstration的链接。Executor窗格显示在编译时产生了一个警告,并且调用了terminate。这个信息可能是从操作系统返回的。
进一步调查发现,terminate()函数默认设置为调用abort(),而不是that this can be changed
然后abort()似乎向操作系统SIGABRT发送了一个信号,该信号在Linux系统上的值为134或6。我发现了关于此问题的冲突信息,我不确定哪个是正确的。
这里有一些参考资料,我会及时更新。
如果你有一个错误代码,那么你就可以在这个错误代码中找到一个错误代码,并将它作为一个结果终止。
https://www.man7.org/linux/man-pages/man7/signal.7.html
https://www.man7.org/linux/man-pages/man3/abort.3.html
我在内联上发布了这个链接,但它也提供了一些关于可以调用terminate()的可能方法的细节。
https://www.ibm.com/docs/en/zos/2.1.0?topic=functions-terminate-function
在我的Linux测试机器上,它使用Linux内核5.10.0-18-amd 64,abort()打印Abortedecho $?打印134

相关问题