The delete
Keyword
We can allocate memory whenever we want on the heap.
In exchange, we have to make sure to deallocate that memory when we are done with it.
You may recall that the new
keyword
- Allocates memory on the heap;
- Initializes the new thing; and,
- Returns a pointer to the new thing.
The opposite of new
is the delete
keyword, which operates in reverse; it
- Takes a pointer to something on the heap;
- Destroys that thing; and,
- Deallocates the memory at that address.
For example,
void existentialMoo(size_t numSpots, size_t age) {
Cow* c = new Cow{numSpots, age}; // "They give birth astride of a grave,
c->moo(); // the light gleams an instant,
delete c; // then it's night once more."
// - Samuel Beckett (Waiting for Godot)
}
int main() {
existentialMoo(4, 2);
return 0;
}
Key Points:
delete
is better read as "delete the thing in memory at ________".- You give it the address of the thing that should be deallocated.
- Nothing happens to the pointer that you give to
delete
.- It even still points to the now deallocated memory!
- Be careful!
- When you are about to lose your last pointer to something, use that pointer to delete it!
In other words, when you write delete cowPtr;
, in your head don't say “delete cowPtr
”, say “delete the Cow
at cowPtr
”.
Hey, Dog, what is the shortest-lived object on the heap?
I don't know, what is the shortest-lived object on the heap?
It's
delete new Cow*{4, 2};
Oh. I thought that was the setup for a joke.
Nope, just an illustration that
delete
takes the same address thatnew
returns!
Deleting Arrays
We have a special keyword for deleting arrays: delete[]
.
This form is important, because it informs the compiler that the pointer does not only point to one thing, but to an entire array.
That way the compiler knows it should destroy and deallocate everything in the array!
A delete
For Every new
In order to prevent memory leaks, your program must perform exactly one delete
for every new
that it performs.
Make sure that you use the matching operators:
new
→delete
new...[]
→delete[]
The delete
statement needs to take the same address that the new
statement returned.
Here's a good rule of thumb: always know when something on the heap will be deleted.
Whenever you use
new
, identify where in the code that thing will bedelete
d and what variable will hold the address.Then go ahead and write that
delete
statement!That way whether you should use
delete
ordelete[]
is fresh in your mind, and you are less likely to forget to do it!
Practice
Consider this program:
double fileMedian(string filename) {
std::ifstream fin{filename};
// First line has the number of datapoints
size_t numDatapoints;
fin >> numDatapoints;
double* data = new double[numDatapoints];
for (size_t i = 0; i < numDatapoints; ++i) {
fin >> data[i];
}
// This is a real thing that you can actually do!
// (it requires the <algorithm> header)
std::sort(data, data + numDatapoints);
double median;
if (numDatapoints % 2 == 1) {
// Return the middle element
median = data[numDatapoints/2];
} else {
// Return the average of the middle 2 elements
median = (data[numDatapoints/2 - 1] + data[numDatapoints/2])/2;
}
return median;
}
int main() {
std::cout << fileMedian("data.txt") << std::endl;
return 0;
}
Assume that data.txt
has 2
on the first line and then two datapoints, 1.0
and 2.0
. Now trace this program using a memory diagram.
(Really do it!)
(No, really! This is an important skill to practice.)
Key Points:
- Memory diagrams can really help prevent memory leaks!
- If you are crossing out the last name of something on the heap, and it hasn't been deallocated, it is about to leak.
- Put a
delete
statement right there!
- Put a
- The diagram can also help you determine which variable to give to
delete
.- It's the variable that contains the address of the thing to destroy and deallocate.
(When logged in, completion status appears here.)