我了解到,如果在堆栈展开期间抛出析构函数程序将会中止,因为那时会传播超过 1 个异常。
这是一个带有注释的示例:
class Foo
{
public:
~Foo()
{
ReleaseResources();
}
private:
int* pInt;
void ReleaseResources()
{
if (!pInt)
throw 0;
else delete pInt;
}
};
int main() try
{
{
Foo local;
throw 1;
} // aborting here, because now 2 exceptions are propagating!
return 0;
}
catch (int& ex)
{
return ex;
}
但是我有一个类层次结构,其中一个析构函数调用一个可能抛出异常的函数,并且由于该条目层次结构被毒化了,这意味着现在所有析构函数都被标记为 noexcept(false)
.
虽然编译器可以插入异常代码,但对于这些类的用户来说是不行的,因为如果发生上述代码示例中的情况,它不会阻止程序中止。
因为我希望析构函数是异常安全的,所以我想到将它们全部标记为 noexcept
但像这样处理析构函数中可能出现的异常:
相同的示例,但经过重新设计,无法中止,并且析构函数异常安全:
class Foo
{
public:
~Foo() noexcept
{
try
{
ReleaseResources();
}
catch (int&)
{
// handle exception here
return;
}
}
private:
int* pInt;
void ReleaseResources()
{
if (!pInt)
throw 0;
else delete pInt;
}
};
int main() try
{
{
Foo local;
throw 1;
} // OK, not aborting here...
return 0;
}
catch (int& ex)
{
return ex;
}
问题是,这是在 destrucotrs 中处理异常的正常方法吗?是否有任何可能使此设计出错的示例?
主要目标是拥有异常安全的析构函数。
还有一个附带问题,在第二个示例中,在堆栈展开期间仍有 2 个异常传播,为什么没有调用中止?如果在堆栈展开期间只允许一个异常?
最佳答案
The question is, is this normal approach to handle exceptions inside destrucotrs? are there any examples that could make this design go wrong?
是的,您可以避免像这样抛出析构函数如果您的//handle exception here
代码实际上处理了异常。但在实践中,如果您在销毁期间抛出异常,通常意味着没有好的方法来处理异常。
从析构函数中抛出意味着某种清理失败。也许资源泄露了,数据无法保存并且现在丢失了,或者某些内部状态无法设置或恢复。无论是什么原因,如果您可以避免或解决问题,您就不必首先抛出问题。
您对这种糟糕情况的解决方案(抛出析构函数)仅在您实际不处于糟糕情况时才有效。在实践中,如果您尝试应用它,您会发现没有什么可写的//handle exception here
,除了可能警告用户或记录问题。
if only one exception is allowed during stack unwinding?
没有这样的规则。在堆栈展开期间抛出的问题是如果未捕获的异常从析构函数中逃逸。如果析构函数在内部抛出并捕获异常,则它不会影响正在进行的堆栈展开。 std::terminate
明确说明堆栈展开何时以终止结束(link):
In some situations exception handling must be abandoned for less subtle error handling techniques. These situations are:
[...]
-- when the destruction of an object during stack unwinding terminates by throwing an exception, or
[...]
关于c++ - 在析构函数中处理异常(但不抛出),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58596480/