题
我们有一个类,其语义行为如下:-
struct Sample
{
~Sample() throw()
{
throw 0;
}
};
void f ()
{
try
{
delete new Sample;
}
catch (...){
}
}
我知道在 dtors 中抛出异常是邪恶的;但是放弃第 3 方库资源会引发异常(但可以立即重新获取,这很奇怪!)。还有一个此类资源的池,例如类 Sample 的数组/容器。因此,有两种情况需要考虑:销毁动态分配的对象和销毁动态分配的对象数组。
目前,仅当使用数组版本(池)时,应用程序才会在不同的执行点随机崩溃。我们认为这是由于内存损坏造成的,但是为什么非池化版本可以工作呢?
分配的内存会发生什么情况?这是未定义的行为吗?如果是数组会发生什么?数组所有元素的 dtor(至少,不是内存)(假设第一个元素的 dtor 抛出异常)是否被调用?
提前致谢,
编辑1:好吧,我们追踪到了一些未被调用的数组元素的 dtor。但是分配的内存好像没有问题...以下是 SC22-N-4411.pdf 的第 5.3.5.7 节)
If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will
call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be
called. [ Note: The deallocation function is called regardless of whether the destructor for the object or some
element of the array throws an exception. —end note ]
<\剪辑>
看起来在这种情况下内存总是被释放。我对标准的解释正确吗?
解决方案
在这种情况下可能会发生两种情况:
- 终止()被调用
- 未定义的行为
在这两种情况下都不能保证动态分配的内存被释放(除非应用程序终止当然会将所有资源返回给操作系统)。
其他提示
C ++将终止应用程序。
由于这是几乎不可能在什么情况下析构函数被调用来确定,标准规则是为从不抛出异常从dtors。
如果您的第三方库是抛出一个异常,捕获它在你的析构函数,记录它,或保存其状态的一些静态缓存在那里你可以把它捡起来“后来”,但不要让它逃脱出来的您的析构函数。
做到这一点,然后看看你的对象集合的工作,它可能会导致您的崩溃。
<强>更新强>
可惜我不是一个规范的律师,喜欢的渔夫之宝方法“吸它的见”。
我会写一个小应用程序与分配一个微克断堆的类。在一个循环中,使类的数组,有类析构函数抛出一个异常,并在循环的末尾抛出一个捕获异常(导致堆栈放松,并调用类的阵列的dtors),看它看到你的虚拟机使用通过屋顶(我敢肯定它会)。
对不起,我不能给你引经据典,但是这是我的“信仰”
既然你在引经据典评论问:
15.2:3有一个注释,说:
“如果一个过程中与一个异常堆栈展开退出称为析构函数终止被称为(15.5.1)。因此,析构函数通常应该捕捉异常,而不是让它们传播出来的析构函数的”
据我可以做出来,对他说“一般”存在的唯一理由,就是它可能非常仔细地编写一个程序,以便没有对象,其析构函数可以抛出,如堆栈展开的一部分被彻底删除。但是,这是一个困难的条件在项目的平均实施,不是“析构函数不能抛出。”
和15.5.1 2说:
“在以下情况下,... - 当对象的堆叠期间展开的破坏(15.2)离开使用异常... void terminate()
被称为”
有用于终止一些其他条件()在15.5.1,这表明你可能希望不要乱扔其他的东西:异常,atexit对处理程序和unexpected
的拷贝构造函数。但是,例如用于拷贝构造函数失败的最可能的原因是内存不足,这在如Linux的可能段错误,而不是反正抛出异常。在这种情况下终止()似乎不坏。
看起来像存储器在这样的情况下总是释放。我是正确解释的标准
看起来以我为虽然为被删除的对象的存储器被始终解除分配。它并不意味着它通过指针拥有,并在其析构函数释放任何存储器,被释放,特别是如果它是一个数组,因而有几个析构函数调用。
哦,是的,和你信任的第三方库是异常安全的?有没有可能是免费的在异常被留在其中它的作者没有预料到的状态的图书馆,并认为,坠机是因为什么?
1)从析构函数抛出异常是不好的,因为如果正在处理异常和发生另一个异常的应用程序将退出。所以,如果在异常处理您的应用程序清除的对象(例如,在他们每个人的调用析构函数)和析构函数之一抛出另一个异常的应用程序将退出。
2)我不认为析构函数获取的元素在容器中的其余时候自动调用其中之一抛出异常。如果异常在容器的析构函数抛出,则元件的其余部分将绝对不被清理作为应用程序将展开堆栈而处理异常。
写析构函数应该是这样的标准方法:
A::~A()
{
try {
// some cleanup code
}
catch (...) {} // Too bad we will never know something went wrong but application will not crash
}
析构函数决不会抛出异常,它会导致不确定的行为,也有可能导致内存泄漏。 让我们看看下面的例子
T* p = new T[10];
delete[] p;
因此,如何将新的[]和删除[]反应如果T抛出析构函数?
让我们首先考虑的是建设的所有进展顺利,那么在删除[]第四个左右的析构函数抛出。 删除[]可以选择传播,这将导致所有留在阵列中的其它T对象丢失,不可检索,因此undestroyable除外。 它也不能“捕获”了异常,因为再删除不会是异常中立的了。
其次,说构造函数之一抛出。说第六构造函数抛出异常。在堆栈展开其已建成到现在为止所有的对象都被解构。因此,第五,第四,第三等的析构函数得到的调用。 如果第4或第3析构函数抛出另一个异常,会发生什么?它应该是 absorded或传播?
有没有答案,所以这个话题会导致不确定的行为。
和如在我的第一示例所概述的,也有可能导致内存泄漏..