C++ Array of pointers: delete or delete []?
delete[] monsters;
Is incorrect because monsters
isn't a pointer to a dynamically allocated array, it is an array of pointers. As a class member it will be destroyed automatically when the class instance is destroyed.
Your other implementation is the correct one as the pointers in the array do point to dynamically allocated Monster
objects.
Note that with your current memory allocation strategy you probably want to declare your own copy constructor and copy-assignment operator so that unintentional copying doesn't cause double deletes. (If you you want to prevent copying you could declare them as private and not actually implement them.)
Deleting array of pointers
No, delete []
is used to delete an array. If you need to delete array elements, you need to call delete
on each one of them.
Proper way to delete an array of pointers
Every pointer allocated with new
gets a corresponding delete
. Every pointer allocated with new []
gets a corresponding delete []
. That's really all you need to know. Of course, when you have a dynamically allocated array which contains dynamically allocated pointers the deallocation must occur in reverse order.
So it follows that the correct idiom would be...
int main()
{
int **container = new int*[n];
for(int i = 0; i < n; ++i)
container[i] = new int[size];
// ... and to deallocate...
for(int i = 0; i < n; ++i)
delete [] container[i];
delete [] container;
}
And then of course I say "stop doing that" and recommend you use a std::array
or std::vector
(and the template type would be unique_ptr<int>
).
C++: How to properly delete array of pointers to pointers in class destructor
In all of the similar questions I found, each element of the array is assigned a value dynamically, using new as such:
arr[i] = new First();
. However, here the elements are assigned the value of a pointer to an object that is a parameter of the function. So, should the destructor delete every element one by one and then delete the array, or is it enough to delete the array?
That, we cannot answer. Does Second
take ownership of the objects passed to .add()
, and if so how were they allocated?
If it does not take ownership, just deleting the array is enough, and that array should be managed by a
std::unique_ptr
doing so for you.If it does take ownership, that argument to
.add()
should be the smart-pointer with the right ownership-semantics and deleter. Your array should then be an array of those smart-pointers, managed by astd::unique_ptr
.
In either case, if you properly use smart-pointers, the default-dtor is fine.
how to properly delete a pointer to array
The requirement to match new[]
with delete[]
is technically correct.
Much better, however (at least in my opinion), would be to forget that you ever even heard of new[]
, and never use it again. I'm pretty sure it's been (at least) 10 years since the last time I used new[]
, and if I'd understood the situation very well, I'd have stopped even sooner than that. Pretty nearly any time you'd even consider using new[]
, an std::vector
(or possibly std::deque
) will be a better choice.
If you're trying to create something roughly equivalent to a vector
or deque
yourself, you don't normally want to use new[]
for that either. They way they (at least normally, though it's possible to change this via a custom Allocator
class) is to allocate "raw" storage with operator new
(which is pretty much like malloc
--you just give it a size, and it gives you that many bytes of storage). Then you use the placement new
operator to create objects in that space, and explicitly invoke the object's destructor to destroy objects in that space.
To give one example, this is what allows std::vector
to support reserve
, which allows you to allocate extra space, which won't be turned into objects until you call something like push_back
or emplace_back
to create an actual object in the space you allocated.
When you use new[]
, it has to create objects of the specified type filling all the space you allocate. You can't create something like push_back
that adds a new object to the collection, because the collection is always already "full". All you can do is allocate a new collection that's larger than the old one (so every addition to the collection is O(N) instead of the amortized O(1) supported by std::vector
--a huge loss of efficiency).
Removing elements from an array of pointers - C++
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:
- use
delete
onarr[2]
to avoid a memory leak, - overwrite
arr[2]
with some other address that's still valid (usingarr[2]
before this step would trigger a seg-fault) - nullify array position copied into
arr[2]
(see below) - decrement the length of your array (e.g.
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]
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.
Remove an object from an array of pointers
Your operator-
is not implemented correctly. Although its loop to find an object is fine, removing that object is broken. You are not updating the array correctly, as you need to shift down ALL of the array elements after the found index, which you are not doing.
Also, make sure your List
class implements the Rule of 3/5/0 correctly. The return value of operator-
is supposed to return a new List
object, so a copy has to be made. You should not be modifying the this
object at all in operator-
(that is the job of operator-=
to do; see What are the basic rules and idioms for operator overloading?).
Also, the buildingNr
member should not be static
. That prevents you from using multiple List
objects at a time.
Try something more like this instead:
#include <algorithm>
class List {
Building* buildingList[10];
int buildingNr;
public:
List() : buildingNr(0) {}
List(const List &src) : buildingNr(src.buildingNr) {
for(int i = 0; i < buildingNr; ++i) {
buildingList[i] = new Building;
buildingList[i]->id = src.buildingList[i]->id;
buildingList[i]->year = src.buildingList[i]->year;
buildingList[i]->price = src.buildingList[i]->price;
buildingList[i]->adress = new char[strlen(src.buildingList[i]->adress)+1);
strcpy(buildingList[i]->adress, src.buildingList[i]->adress);
}
}
~List() {
for(int i = 0; i < buildingNr; ++i) {
delete[] buildingList[i]->adress;
delete buildingList[i];
}
}
List& operator=(const List &rhs) {
if (this != &rhs) {
List tmp(rhs);
std::swap_ranges(buildingList, buildingList+10, tmp.buildingList);
std::swap(buildingNr, tmp.buildingNr);
}
return *this;
}
List& operator-=(int id) {
for(int i = 0; i < buildingNr; ++i) {
if (buildingList[i]->getId() == id) {
delete buildingList[i];
for(int j = i + 1; j < buildingNr; ++j) {
buildingList[j - 1] = buildingList[j];
}
buildingList[buildingNr] = NULL;
--buildingNr;
cout << "Building " << i << " was deleted." << endl;
return *this;
}
}
throw -1;
}
...
};
friend List operator-(List lhs, int id) {
lhs -= id;
return lhs;
}
Related Topics
Get Current Working Directory in a Qt Application
How to Get a List of Files in a Folder in Which the Files Are Sorted with Modified Date Time
Using Zeromq Together with Boost::Asio
How to Asynchronously Copy Memory from the Host to the Device Using Thrust and Cuda Streams
Correct Way to Inherit from Std::Exception
Why Function Objects Should Be Pass-By-Value
Filestorage for Opencv Python API
How to Force Gcc to Link Unreferenced, Static C++ Objects from a Library
How to Catch Out of Memory Exception in C++
Alternative to C++ Static Virtual Methods
Template Tricks with Const Char* as a Non-Type Parameter
Sse-Copy, Avx-Copy and Std::Copy Performance
Concatenate Compile-Time Strings in a Template at Compile Time
In C++ I Cannot Grasp Pointers and Classes
C++ Linux Double Destruction of Static Variable. Linking Symbols Overlap
Visual Studio 2015 Gives Me Errors Upon Creating a Simple Test Console Program