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

C++ 无抛出异常保证和堆栈溢出,c++,exception-handling,exception-safety,C++,Exception Handling,Exception Safety,有几个特殊功能通常保证不会抛出例外,例如: 析构函数 swap方法 考虑以下swap实现,如中所述: 它使用两个swap函数-用于整数和指针。如果第二个函数会导致堆栈溢出怎么办?对象将被损坏。我猜这不是一个std::异常,而是某种系统异常,比如Win32异常。但是现在我们不能保证没有抛出,因为我们调用的是一个函数 但是所有权威来源都只需使用swap就可以了,这里不会抛出任何异常。为什么?通常,您无法处理堆栈耗尽的问题。标准没有说明如果堆栈用完会发生什么,也没有说明堆栈是什么,可用的数量等等。

有几个特殊功能通常保证不会抛出例外,例如:

  • 析构函数
  • swap
    方法
考虑以下
swap
实现,如中所述:

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


但是所有权威来源都只需使用
swap
就可以了,这里不会抛出任何异常。为什么?

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

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


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

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

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

如果您担心,可以将
swap
函数标记为
noexcept
。然后在一致性实现中,任何试图离开函数的异常都将导致程序
终止()。也就是说,它完成了
noexcept
合同,代价是取出您的程序

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

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

但现在我们不能保证不扔

我从未遇到过在这种状态下会抛出异常的实现,如果它真的抛出异常,我会非常害怕

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

我读过的权威资料(比如说)并不是“像使用它一样使用它”;他们说,如果您有(例如)一个非抛出
交换
函数和一个非抛出析构函数,那么您可以从使用它们的函数中提供异常安全保证

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

  • 基本:异常使所有内容处于有效但未指定的状态
  • Strong:异常保持状态不变
  • 不抛出:不会抛出异常
提供“强有力”担保的常见方法是:

  • 做那些可能会产生临时状态副本的工作
  • 用活动状态交换该副本(需要非抛出交换操作)
  • 销毁旧状态(需要非抛出析构函数)
如果你没有从这些操作中获得免丢的保证,那么提供一个强有力的保证就更困难,甚至可能是不可能的

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

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

但现在我们不能保证不扔

我从未遇到过在这种状态下会抛出异常的实现,如果它真的抛出异常,我会非常害怕

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

我读过的权威资料(比如说)并不是“像使用它一样使用它”;他们说,如果您有(例如)一个非抛出
交换
函数和一个非抛出析构函数,那么您可以从使用它们的函数中提供异常安全保证

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

  • 基本:异常使所有内容处于有效但未指定的状态
  • Strong:异常保持状态不变
  • 不抛出:不会抛出异常
而不是普通的方法
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?
}