如果delete [] p失败会怎么样?

时间:2011-06-28 13:01:23

标签: c++ arrays exception memory-leaks resource-management

假设我有一个指向动态分配的10个元素数组的指针:

T* p = new T[10];

后来,我想发布那个数组:

delete[] p;

如果其中一个T析构函数抛出异常,会发生什么?其他元素是否仍然被破坏?记忆会被释放吗?是否会传播异常,还是会终止程序执行?

同样地,当std::vector<T>被销毁并且其中一个T析构函数抛出时会发生什么?

6 个答案:

答案 0 :(得分:6)

我无法在标准中明确地看到它:

只是他们将按照创建的相反顺序被调用

5.3.5删除[expr.delete]

  

6如果delete-expression的操作数的值不是空指针值,则delete-expression将调用该对象的析构函数(如果有)或要删除的数组的元素。在数组的情况下,元素将按照地址递减的顺序被销毁(即,与构造函数完成的顺序相反;见12.6.2)。

即使抛出异常,也会完成内存释放:

  

7如果delete-expression的操作数值不是空指针值,则delete-expression将调用释放函数(3.7.4.2)。否则,未指定是否将调用释放功能。 [注意:无论对象的析构函数或数组的某个元素是否引发异常,都会调用释放函数。 - 结束说明]

我在G ++中尝试了以下代码,它表明在异常之后不再调用析构函数:

#include <iostream>
int id = 0;
class X
{
    public:
         X() {   me = id++; std::cout << "C: Start" << me << "\n";}
        ~X() {   std::cout << "C: Done " << me << "\n";
                 if (me == 5) {throw int(1);}
             }
    private:
        int me;
};

int main()
{
    try
    {
        X       data[10];
    }
    catch(...)
    {
        std::cout << "Finished\n";
    }
}

执行:

> g++ de.cpp
> ./a.out
C: Start0
C: Start1
C: Start2
C: Start3
C: Start4
C: Start5
C: Start6
C: Start7
C: Start8
C: Start9
C: Done 9
C: Done 8
C: Done 7
C: Done 6
C: Done 5
Finished

所有这些都回到了这个(很老的答案):
throwing exceptions out of a destructor

答案 1 :(得分:5)

永远不要那样做。如果已存在活动例外,则会调用std::terminate"Bang, you're dead"。你的析构函数必须。不。扔。抗蚀剂。


编辑:标准的相关部分(14882 2003), 15.2 构造函数和析构函数[except.dtor]

  

15.2.3 为从try块到try的路径构造的自动对象调用析构函数的过程     throw-expression被称为“stack unwinding”。[注意:如果在堆栈展开期间调用的析构函数以异常退出,则调用terminate(15.5.1)。所以析构函数通常应该捕获异常和     不要让它们传播出析构函数。 - 后注]


玩游戏的测试用例(在现实生活中,抛出从std::exception派生的东西,永远不会抛出int或其他东西!):

    #include <iostream>
    int main() {
        struct Foo {
            ~Foo() {
                throw 0; // ... fore, std::terminate is called.
            }
        };

        try {
            Foo f;
            throw 0; // First one, will be the active exception once Foo::~Foo()
                     // is executed, there- ...
        } catch (int) {
            std::cout << "caught something" << std::endl;
        }
    }

答案 2 :(得分:5)

  

5.3.5.7如果是操作数的值   delete-expression不是null   指针值,delete-expression   将调用释放功能   (3.7.3.2)。否则,它是未指定的   是否释放功能   叫做。 [注:取消分配   无论如何调用函数   是否为对象的析构函数   或者数组的某个元素抛出一个   例外。 - 结束说明]

除了

之外,找不到任何关于析构函数的内容
  

对于数组,元素将是   按地址递减的顺序销毁(即按照构造函数完成的相反顺序销毁;见12.6.2)。

我想在投掷之后没有更多的析构函数被调用,但我不确定。

答案 3 :(得分:2)

要回答你的第二个问题,如果你使用了std :: vector,那么就不需要调用delete了,你没有使用指针(我相信内部的vector类,但这不是由你来管理)。

答案 4 :(得分:2)

好的,这是一些实验性代码:

#include <cstddef>
#include <cstdlib>
#include <new>
#include <iostream>

void* operator new[](size_t size) throw (std::bad_alloc)
{
    std::cout << "allocating " << size << " bytes" << std::endl;
    return malloc(size);
}

void operator delete[](void* payload) throw ()
{
    std::cout << "releasing memory at " << payload << std::endl;
    free(payload);
}

struct X
{
    bool throw_during_destruction;

    ~X()
    {
        std::cout << "destructing " << (void*)this << std::endl;
        if (throw_during_destruction) throw 42;
    }
};

int main()
{
    X* p = new X[10]();
    p[5].throw_during_destruction = true;
    p[1].throw_during_destruction = true;
    delete[] p;
}

运行代码在g ++ 4.6.0上提供了以下输出:

allocating 14 bytes
destructing 0x3e2475
destructing 0x3e2474
destructing 0x3e2473
destructing 0x3e2472
destructing 0x3e2471
terminate called after throwing an instance of 'int'

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

因此,只要第一个析构函数抛出就会立即调用std::terminate。其他元素没有被破坏,内存也没有被释放。谁能证实这一点?

答案 5 :(得分:0)

如果抛出异常,则抛出异常。未能破坏的对象显然不会正确销毁,并且数组中没有剩余的对象。

如果使用向量,则问题是相同的,而不是在代码中。 : - )

因此,抛出析构函数只是一个坏主意(tm)。


就像下面的@Martin所示,一旦我们进入析构函数,抛出的对象就会正式不存在。其他人可能也会回忆起他们的记忆。

然而,它显然包含一些复杂的东西,不适合冲洗冲洗。如果该对象以及数组中跟随它的其他对象包含一些互斥锁,打开文件,数据库缓存或shared_ptrs,并且没有一个他们的析构函数运行,那么我们可能会遇到大麻烦。

在那个时候调用std :: terminate,让程序摆脱它的痛苦,看起来像你想要的东西!