无抛出异常保证和堆栈溢出

no-throw exception guarantee and stack overflow

本文关键字:堆栈 栈溢出 抛出异常      更新时间:2023-10-16

有几个特殊的函数通常保证不抛出异常,例如:

  • 析构函数
  • swap方法

考虑以下swap实现,如本回答所述:

friend void swap(dumb_array& first, dumb_array& second)
{
    using std::swap; 
    swap(first.mSize, second.mSize);  
    swap(first.mArray, second.mArray);  // What if stack overlow occurs here?
}

它使用了两个swap函数——整数函数和指针函数。如果第二个函数会导致堆栈溢出怎么办?对象将被损坏。我猜它不是std::exception,它是某种系统异常,像Win32-exception。但是现在我们不能保证不抛出,因为我们调用的是一个函数。

但是所有的权威来源只是使用swap,就像它是好的一样,这里不会抛出任何异常。为什么?

一般情况下,您无法处理耗尽堆栈的问题。标准没有说明如果用完堆栈会发生什么,也没有说明堆栈是什么,有多少可用,等等。操作系统可能会让你在构建可执行文件时或运行时控制它,如果你正在编写库代码,所有这些都是相当无关的,因为你无法控制进程有多少堆栈,或者在用户调用库之前已经使用了多少堆栈。

可以假设堆栈溢出导致操作系统在程序外部执行一些操作。一个非常简单的操作系统可能会让它变得奇怪(未定义的行为),一个严重的操作系统可能会把这个过程吹走,或者如果你真的很不幸,它会抛出一些实现定义的异常。我实际上不知道Windows是否为堆栈溢出提供了SEH异常,但如果有,那么最好不要启用它。

如果您担心,您可以将swap函数标记为noexcept。然后,在符合标准的实现中,任何试图离开函数的异常都会导致程序返回terminate()。也就是说,它以取出您的程序为代价来履行noexcept契约。

如果第二个函数会导致堆栈溢出怎么办?

那么您的程序处于不可恢复的故障状态,并且没有实际的方法来处理这种情况。希望溢出已经造成了分段错误并终止了程序。

但是现在我们不能保证不扔

我从来没有遇到过在这种状态下抛出异常的实现,如果它真的发生了,我会很害怕的。

但是所有权威的来源只是使用swap,就像它是ok的一样,这里不会抛出任何异常。为什么?

我读过的权威来源(比如这篇)不会"随便用就行";他们说如果你有(例如)一个非抛出的swap函数和一个非抛出的析构函数,那么你可以为使用它们的函数提供异常安全保证。

根据异常保证对函数进行分类是很有用的:

  • 基本:异常使所有内容处于有效但未指定的状态
  • Strong:异常保持状态不变
  • no -throw:不抛出异常。

提供"强"保证的常用方法是:

  • 执行可能会抛出状态
  • 的临时副本的工作
  • 用活动状态交换副本(需要非抛出交换操作)
  • 销毁旧状态(需要一个非抛出的析构函数)

如果对这些操作没有无抛出保证,那么提供一个强有力的保证就会更加困难,甚至可能是不可能的。