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

时间:2013-04-18 10:25:15

标签: c++ exception-handling exception-safety

有几个特殊功能通常可以保证不会抛出激活,例如:

  • 析构
  • swap方法

考虑以下swap实施,如this answer中所述:

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就好了,这里不会抛出异常。为什么呢?

2 个答案:

答案 0 :(得分:4)

通常,您无法处理堆栈耗尽。标准没有说明如果你的堆栈耗尽会发生什么,它也不会谈论堆栈是什么,可用的数量等等。操作系统可以让你在构建可执行文件或运行时控制它如果您正在编写库代码,那么所有这些都是无关紧要的,因为您无法控制该进程有多少堆栈,或者在用户调用您的库之前已经使用了多少堆栈。

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

如果您担心,可以将swap功能标记为noexcept。然后在符合要求的实现中,任何试图离开该函数的异常都将导致程序terminate()。也就是说,它以取消你的程序为代价来履行noexcept合同。

答案 1 :(得分:2)

  

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

然后你的程序处于不可恢复的故障状态,并且没有切实可行的方法来处理这种情况。希望溢出已经导致了分段错误并终止了程序。

  

但现在我们不能保证不投掷

我从未遇到过会在该状态下抛出异常的实现,如果有的话,我会感到非常害怕。

  

但是所有权威来源只是使用交换就好了,这里不会抛出异常。为什么呢?

我读过的权威来源(例如this one)并不“只是使用它就好了”;他们说 if 你有(例如)非投掷swap函数和非投掷析构函数,然后你可以提供异常安全保证来自使用它们的函数。

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

  • 基本:异常使一切处于有效但未指定的状态
  • 强:异常让状态保持不变
  • 不抛:不会抛出异常。

提供“强有力”保证的常用方法是:

  • 执行可能会投放到州的临时副本的工作
  • 将该副本与实时状态交换(需要非投掷交换操作)
  • 摧毁旧状态(需要非投掷析构函数)

如果您没有这些操作的无投保证,那么提供强有力的保证将更加困难,也许是不可能的。