简体   繁体   中英

Removing elements from an array of pointers - C++

So I had an array of structure pointers. It looked something like this:

index                 [0   1   2   3   4]
value of structure     21  7   42  30  NULL

I was trying to remove the value stored at index 2 (42). I thought that since each element of this array was a pointer to a structure, then in order to delete 42, I would have to first call delete on arr[2], then I would say arr[2] = arr[3] and then delete on arr[3], then arr[3] = arr[4]. That wasn't working so I just decided to try it without the delete keyword and just do arr[2] = arr[3] and arr[3] = arr[4], and it worked. So my question is, why did I not have to use the delete keyword in order to do this. I was thinking that if I just set arr[2] to arr[3], then the structure being pointed to by arr[2] would be lost, and I would get a memory leak. Is that not the case?

I thought that since each element of this array was a pointer to a structure, then in order to delete 42, I would have to first call delete on arr[2]

Yes, you would use delete on arr[2] in order to free the memory to which arr[2] is pointing.

then I would say arr[2] = arr[3]

So far so good.

and then delete on arr[3]

This is the problem right here. Suppose you have code like this:

int arr_cnt = 5;
int *arr[arr_cnt];
for(int i = 0; i < arr_cnt; ++i)
    arr[i] = new int(i+arr_cnt);  // using ints for simplicity, but concept is the same

Your array arr now looks like this:

idx   *arr[]     values in heap, due to using 'new' operator
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | c---|----> 7
    +-----+
3   | d---|----> 8
    +-----+
4   | e---|----> 9
    +-----+

Where the letters represent different memory addresses returned by new .

This means that, to properly remove element at index 2, you need to:

  1. use delete on arr[2] to avoid a memory leak,
  2. overwrite arr[2] with some other address that's still valid (using arr[2] before this step would trigger a seg-fault)
  3. nullify array position copied into arr[2] (see below)
  4. decrement the length of your array (eg arr_cnt--; )

In other words:

delete arr[2];      // step 1, address 'c' no longer valid
arr[2] = arr[4];    // step 2, arr[2] is now 'e', which points to 9 just like arr[4]
arr[4] = NULL;      // step 3, arr[4] is now invalid
--arr_cnt;          // step 4

The diagram would now look like this:

idx   *arr[]     values in heap, due to using 'new' operator
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | e---|-----------+   // address 'e' used to be in arr[4]
    +-----+           |
3   | d---|----> 8    |
    +-----+           |
4   | nil |      9 <--+
    +-----+

then arr[3] = arr[4]. That wasn't working

If you follow the diagram, you've probably noticed by now that using delete on both meant that you were invalidating two entries. In other words, if we skip the 2nd diagram and try your delete arr[2]; arr[2] = arr[3]; delete arr[3] delete arr[2]; arr[2] = arr[3]; delete arr[3] delete arr[2]; arr[2] = arr[3]; delete arr[3] logic, you end up with:

delete arr[2];
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | c---|----> ?  // invalidated
    +-----+
3   | d---|----> 8
    +-----+
4   | e---|----> 9
    +-----+

arr[2] = arr[3];
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | d---|-+
    +-----+ |
3   | d---|-+--> 8  // both have the same address, so point to the same place
    +-----+
4   | e---|----> 9
    +-----+

delete arr[3];
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | d---|-+
    +-----+ |
3   | d---|-+--> ?  // both invalid now, but not set to null
    +-----+
4   | e---|----> 9
    +-----+

so I just decided to try it without the delete keyword and just do arr[2] = arr[3] and arr[3] = arr[4], and it worked.

But now you have a memory leak. You always have to delete every new in C++, just like you always have to free every malloc in C.

So my question is, why did I not have to use the delete keyword in order to do this.

You do have to use it. The problem is that you were invalidating more than you thought you were and ended up trying to work with memory that had been deallocated. When a program tries to access memory like this, it causes a segmentation fault. I don't remember the exact message Windows would display, but it's probably an unhandled exception message of some kind. (Segmentation fault tends to be GNU/Linux terminology.)

I was thinking that if I just set arr[2] to arr[3], then the structure being pointed to by arr[2] would be lost, and I would get a memory leak. Is that not the case?

You're correct here. The problem is not your understanding of the new / delete relationship, only the fact that the assignment you described is a shallow copy and you ended up deleting more than expected.

Check to make sure you truly have an array of structure pointers. Also if you do have an array of structure pointers after you delete element at index 2 do not call delete on 3 or the indexes after. Just arr[2] = arr[3]; arr[3] = arr[4]; It's not a memory leak because you are just copying a pointer to the structure not the actual structure.

struct A
    {
    };


//This is an array of A structures
A * array1;

//This is an array of pointers to A structures
A** array2;

I would have to first call delete on arr[2], then I would say arr[2] = arr[3] and then delete on arr[3], then arr[3] = arr[4].

You need to simply delete arr[2] and shift all items to the left, without deleting them. If you apply delete to all consequent objects, then you will loose them all.

Of course, you can simply shift an array without deleting arr[2] , but this will cause a memory leak. The object will not be disposed.

The problem you have is that you delete pointers you should not delete.

After you deleted the old pointer at index 2, and assign the at index 3 to index 2, you have two pointers pointing to the same object: At index 2 and 3. Deleting the object pointed to by index 3 delete the object that both pointers point to, making the pointer in index 2 invalid.

The solution to your problem (except switching to std::vector which is what I recommend) is to delete the first object in index 2, move down the values from the higher indexes (eg array[2] = array[3] ) and set the last pointer to nullptr . No more deleting needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM