有几个特殊的函数通常保证不抛出异常,例如:
- 析构函数
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:不抛出异常。
提供"强"保证的常用方法是:
- 执行可能会抛出状态 的临时副本的工作
- 用活动状态交换副本(需要非抛出交换操作)
- 销毁旧状态(需要一个非抛出的析构函数)
如果对这些操作没有无抛出保证,那么提供一个强有力的保证就会更加困难,甚至可能是不可能的。