Does it delete each index individually in a unique way?

I'm trying to create an array of class data type pointers Pool

, but deleting it as if it were an array doesn't work.

This is the code:

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;
}

      

As I understand it, my string Pool *pools[]

creates an array of pool pointers. And this is exactly what my IDE says what I am doing. I run the code without issue until I get to the delete statement.

delete[]

is causing me problems for some reason , but removing each one is not done individually:

What's going on and how can I delete delete []?

+3


source to share


6 answers


The only way to remove each item with one command is to do it std::vector<std::unique_ptr<Pool>>

and call it pools.clear()

.

All other solutions require you to iterate over the elements and remove them separately.


Also, your functions create*Pool()

shouldn't return original pointers. They must return smart pointers for two reasons:

  • Users will then know that they are responsible for deleting the pointer.

    (With raw pointers, they'll have to look at internal implementations and / or documentation to know.)

  • This makes it impossible to leak memory because the user now doesn't have to forget to call delete

    somewhere, which can be very difficult to get in a complex project.

So I would recommend returning std::unique_ptr

:



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

      

or better yet (in C ++ 14):

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

      

Now, even if you store smart pointers in your original static array:

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

      

they will be delete

pointer driven when the array goes out of scope.

+7


source


"What's going on and how can I get it to delete[]

work?"



Of course, delete[]

either delete

without the preceding operator new[]

or new

does not work.
Calling any form delete

, if the memory was not dynamically allocated, causes undefined behavior.

+4


source


You would use delete[]

if you were creating a dynamic array Pool

s. But what (I think) you are doing here is creating a static array of pointers to dynamic Pool

s. So instead of delete[]

using everything as if it were a dynamic array, which is not the case, you need to make delete

every single pointer in the array as you accidentally discovered.

Note, however, that deleting each item is clearly verbose and fragile; better to skip them like this:

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

      

or, even better, like this:

for (Pool *p: pools)
    delete p;

      

+4


source


As I understand it, my string Pool *pools[]

creates an array of pool pointers.

Right. In particular, a local static array of 3 Pool*

pointers.

delete[]

is causing me problems for some reason , but deleting each one individually doesn't

You are not allocating the array with new[]

, so you cannot free it with delete[]

. It lives on the stack, not the heap, and thus will be recovered when the array is out of scope.

However, your array elements are pointers to dynamically allocated objects Pool

and need to be freed using delete

no matter how the array is allocated. Even if the array was allocated dynamically with new[]

, you still need delete

separate objects Pool

before freeing the array with delete[]

.

What's going on and how can I get it to delete[]

work?

If you really want to use delete[]

, you should use 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;

      

Otherwise, you should use std::vector

instead 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));

      

Or:

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)));

      

To free the pools, you can use a loop:

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

      

Or:

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

      

Or:

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

      

Or:

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

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

      

If you are using C ++ 11, you can use std::unique_ptr<Pool>

instead Pool*

as elements std::vector

and exclude the call delete

manually ( std::auto_ptr

not safe to use in STL containers, so don't try):

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

      

However, since you have a small, fixed number of pools, an array of elements std::unique_ptr<Pool>

(C ++ 11 and later) or std::auto_ptr<Pool>

will have less overhead than 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;
}

      

By the way on the side of the note, you create your pools in reverse order. Your enum is ordered as small / medium / large, but your array is ordered as large / medium / small. You need to swap createLargePool()

and createSmallPool()

as shown above.

+4


source


As mentioned in the comments, you cannot call delete [] on an array that has not been allocated in the free-store with the new []. However, to answer another aspect of your posed question, there is a better way to automatically distribute everything. Instead of an array, use std::vector<Pool> pools

and for each pool just pools.push_back( createMediumPool(Vector3i(12, 45)) )

, etc.

Then, when the pools go out of scope, it will automatically call the destructor of every pool it contains, and all deallocations will be done automatically. As an added bonus, if you throw an error and at some point you get a "bad trip" in your code, it will still automatically free all resources. This is the Resource Allocation Initialization (RAII) principle.

+1


source


Your array is allocated on the stack and doesn't need to be removed. Stack-based variables should not be, and in fact should not be removed. Only delete is used to clean up heap-based objects. Let's take this example (assume you have a typical 32 bit system and sizeof (int) == 4 bytes)

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 wrong with you.
}

      

The compiler generates code for managing stack-based variables. Spaced is allocated on the stack at the start of the function and returns after the function returns.

Objects on the heap follow different lifecycle rules. You must remove them when you're done with them.

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.
}

      

Your code example gets a little more complicated because you have a stack based array that contains heap based objects. You need to delete individual heap-based objects in the array, but not the array itself.

Here's an example that shows different flavors

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;
}

      

0


source







All Articles