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:
deleteis 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
deletetakes the same address thatnewreturns!
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→deletenew...[]→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 bedeleted and what variable will hold the address.
Then go ahead and write that
deletestatement!
That way whether you should use
deleteordelete[]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
deletestatement 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.)