唯一的方法是单独删除每个索引吗?

时间:2015-07-23 20:58:36

标签: c++

我正在尝试创建类数据类型Pool的指针数组,但删除它就好像它是一个数组一样不起作用。

这是代码:

struct Pool {
    /* in reality, complicated stuff here */
    int size;
};

Pool* createLargePool() { return new Pool{100}; }
Pool* createMediumPool() { return new Pool{20}; }
Pool* createSmallPool() { return new Pool{5}; }

int main() {
    using namespace std;

    enum ePool{
        small,
        medium,
        large,
        last
    };

    Pool *pools[ePool::last] = {
        createLargePool()
        , createMediumPool()
        , createSmallPool()
    };

    //Individually works. 
    delete pools[0];
    delete pools[1];
    delete pools[2];
    //delete[] pools;// error. (assertion error?)
    system("pause");
    return 0;
}

根据我的理解,我的行Pool *pools[]正在创建一个Pool指针数组。这正是我的IDE所说的我正在做的事情。在我到达删除声明之前,我没有遇到任何问题。

由于某种原因delete[]导致我出现问题,但是单独删除每个问题不会:

发生了什么以及如何让删除[]工作?

6 个答案:

答案 0 :(得分:7)

使用单个命令删除每个元素的唯一方法是将其设为std::vector<std::unique_ptr<Pool>>并调用pools.clear()

所有其他解决方案都要求您循环遍历元素并单独删除它们。

此外,您的create*Pool()函数不应返回原始指针。他们应该返回智能指针,原因有两个:

  • 然后,用户知道他们负责删除指针。

    (使用原始指针,他们必须查看内部实现和/或文档才能知道。)

  • 这使得内存泄漏几乎不可能,因为用户现在不必记得在某个地方调用delete,这在复杂的项目中很难正确。

    < / LI>

所以我建议您返回std::unique_ptr

std::unique_ptr<Pool> createLargePool() { return std::unique_ptr<Pool>(new Pool{100}); }

或更好(在C ++ 14中):

std::unique_ptr<Pool> createLargePool() { return std::make_unique<Pool>(100); }

现在,即使您将智能指针存储在原始静态数组中:

std::unique_ptr<Pool> pools[ePool::last] = {
    createLargePool()
    , createMediumPool()
    , createSmallPool()
};

当数组超出范围时,它们将delete它们的托管指针。

答案 1 :(得分:4)

  

&#34;发生了什么以及如何让delete[]工作?&#34;

当然delete[]delete没有先前的new[]new声明无效
如果内存未动态分配,则调用delete的任一形式,引发未定义的行为。

答案 2 :(得分:4)

如果您要创建delete[]的动态数组,则可以使用Pool。但是你在这里做的事情(在我看来)正在创建一个指向动态Pool静态指针数组。所以,而不是delete[]整个事情就好像它是一个动态数组,它不是,你需要做的是delete数组中的每个单独指针,就像你一样不经意间发现了。

但请注意,明确删除每个元素都是冗长而脆弱的;最好像这样循环遍历:

for (int i = 0; i < ePool::last; ++i)
    delete pools[i];

或者更好,像这样:

for (Pool *p: pools)
    delete p;

答案 3 :(得分:4)

  

根据我的理解,我的行Pool *pools[]正在创建一个Pool指针数组。

正确。具体来说,是一个3个Pool*指针的本地静态数组

  

由于某些原因delete[]导致我出现问题,但单独删除每个问题

您没有使用new[]分配数组,因此您无法使用delete[]释放它。它存在于堆栈上,而不是堆上,因此当数组超出范围时将被回收。

但是,您的数组元素是指向动态分配的Pool个对象的指针,无论数组是如何分配的,都需要使用delete释放这些对象。即使数组是使用new[]动态分配的,在使用delete释放数组之前,您仍然需要Pool个别delete[]个对象。

  

发生了什么以及如何让delete[]工作?

如果您真的想使用delete[],则必须使用new[]

Pool** pools = new Pool*[ePool::last];
pools[0] = createLargePool(Vector3i(12, 45));
pools[1] = createMediumPool(Vector3i(12, 45));
pools[2] = createSmallPool(Vector3i(12, 45));

...

delete pools[0];
delete pools[1];
delete pools[2];
delete[] pools;

否则,您应该使用std::vector代替new[]

std::vector<Pool*> pools(ePool::last);
pools[0] = createLargePool(Vector3i(12, 45));
pools[1] = createMediumPool(Vector3i(12, 45));
pools[2] = createSmallPool(Vector3i(12, 45));

或者:

std::vector<Pool*> pools;
pools.reserve(ePool::last);
pools.push_back(createLargePool(Vector3i(12, 45)));
pools.push_back(createMediumPool(Vector3i(12, 45)));
pools.push_back(createSmallPool(Vector3i(12, 45)));

要释放池,您可以使用循环:

// C++11 and later only...
for (Pool* pool : pools)
    delete pool;

或者:

for (int i = ePool::small; i < ePool::last; ++i)
    delete pools[i];

或者:

for (std::vector<Pool*>::iterator iter = pools.begin(); iter != pools.end(); ++iter)
    delete *iter;

或者:

void freePool(Pool *pool)
{
    delete pool;
}

std::for_each(pools.begin(), pools.end(), freePool);

如果您使用的是C ++ 11,则可以使用std::unique_ptr<Pool>代替Pool*作为std::vector元素,并取消手动调用delete std::auto_ptr在STL容器中使用是不安全的,所以不要尝试):

std::vector<std::unique_ptr<Pool>> pools;
...

但是,由于您有一小部分池,因此std::unique_ptr<Pool>(C ++ 11及更高版本)或std::auto_ptr<Pool>元素的数组开销将小于std::vector

#include <memory>

int main()
{
    std::unique_ptr<Pool> pools[ePool::last];
    pools[ePool::small] = createSmallPool();
    pools[ePool::medium] = createMediumPool();
    pools[ePool::large] = createLargePool();
    /*
    std::auto_ptr<Pool> pools[ePool::last];
    pools[ePool::small].reset(createSmallPool());
    pools[ePool::medium].reset(createMediumPool());
    pools[ePool::large].reset(createLargePool());
    */

    ...

    system("pause");
    return 0;
}

顺便说一句,顺便提一下,您正在按向后顺序创建池。您的枚举按小/中/大订购,但您的阵列按大/中/小订购。您需要交换createLargePool()createSmallPool(),如上所示。

答案 4 :(得分:1)

如评论中所述,您无法在未使用new []在免费商店上分配的数组上调用delete []。但是,要回答您提出的问题的其他方面,有一种更好的方法可以自动解除所有内容。使用std::vector<Pool> pools而不是数组,对于每个池,只需执行pools.push_back( createMediumPool(Vector3i(12, 45)) )等...

然后,当池超出范围时,它会自动调用它包含的每个池的析构函数,并且所有解除分配都将自动发生。作为一个额外的好处,如果你在某个时候抛出错误并采取代码的“不愉快路径”,它仍然会自动释放所有资源。这是资源分配初始化(RAII)原则。

答案 5 :(得分:0)

您的数组已在堆栈中分配,无需删除。基于堆栈的变量不必是,实际上不应该被删除。您只能使用delete来清理基于堆的对象。举个例子(假设你有一个典型的32位系统,那个sizeof(int)== 4个字节)

void foo() {
    int x    = 10;             // x on stack. consumes 4 bytes
    int y[3] = { 29, 42, 17};  // y on stack. consumes 12 bytes
    int z    = 11;             // z on stack. another 4 bytes

    cout << "Address of x    = " << &x    << endl;
    cout << "Address of y[0] = " << &y[0] << endl;
    cout << "Address of y[1] = " << &y[1] << endl;
    cout << "Address of y[2] = " << &y[2] << endl;
    cout << "Address of z    = " << &z    << endl;

    delete x; // NO! Don't delete a stack based variable
    delete y; // NO! Don't delete him either
    delete z; // NO! What's wrong with you.
}

编译器生成用于管理基于堆栈的变量的代码。 Spaced在函数开始时在堆栈上分配,并在函数返回时回收。

堆上的对象遵循不同的生命周期规则。完成后,您可以删除它们。

void foo() {
    int *x   = new int;      // x points to a 4 byte memory block on the heap
    int *y   = new int[3];   // y points to a 12 byte memory block on the heap

    delete x;   // Yes. Thank you.
    delete []y; // Correct.
}

您的代码示例有点复杂,因为您有一个基于堆栈的数组,它包含基于堆的对象。您需要删除数组中的各个基于堆的对象,而不是数组本身。

这是一个显示各种口味的例子

struct Pool {
   int x;
};

int main() {
    Pool p1[2] = {1,2} ; // array of objects. Everything lives on the stack.
    Pool* p2[2] = {new Pool, new Pool} ; // array of pointers. Array on stack. Pools in it live on the heap
    Pool *p3 = new Pool[2]; // heap based array of objects.
    p3[0].x=1;
    p3[1].x=7;
    Pool **p4 = new Pool*[2]; // heap based array of pointers to objects.
    p4[0] = new Pool;
    p4[1] = new Pool;


    // cleanup
    // none for p1
    delete p2[0];  // delete the heap based objects inside p2
    delete p2[1];

    delete [] p3;  // delete the heap of objects

    delete p4[0]; // delete pointers inside p4
    delete p4[1];
    delete []p4;  // plus the heap based array;
    return 0;
}