Skip to main content

Section 22.5 Memory Leaks and Delete

When our program is in foo(), the memory looks like this:
Memory layout with p1 and p2 that point at allocated heap memory
When foo returns, its stack frame disappears. That means p1 and p2 will go away and so will our only record of where the memory allocated for 5 and "Hello World" is. The heap memory persists, but is no longer locatable by the program. This is called a memory leak. A memory leak is not immediately harmful. Whatever memory has been leaked will just sit around on the heap until the program finishes, at which time it is recovered.
The memory diagram for this situation might look like:
Leaked memory on the heap has no pointers that point to it
However, in a long running program that keeps leaking memory, it will get harder and harder to find an empty block of space on the heap when a new allocation is made. Eventually, we will run out of memory and the program will crash. If you have ever played a video game for an extended period of time and noticed that it started to slow down before eventually crashing, you probably encountered a memory leak.
To avoid memory leaks, we need to deallocate (or free) the memory we have allocated on the heap. In C++, we do this with the delete operator followed by a pointer that points at the memory we wish to delete. Something like delete p1;. Here is a version of the program that avoids leaking memory:
#include <iostream>
#include <string>

using namespace std;

void foo() {
    int y = 2;
    
    int* p1 = new int(5);
    string* p2 = new string("Hello, world");

    cout << "p1: " << p1 << endl;
    cout << "p1 points to: " << *p1 << endl;
    cout << "p2: " << p2 << endl;
    cout << "p2 points to: " << *p2 << endl;

    delete p1;
    delete p2;
    // --end of foo()--
}

int main() {
    int x = 1;
    foo();
    // --after foo() returns--
}
In this version, we call delete on p1 and p2 before foo returns. This properly deallocates the memory we allocated with new, preventing any memory leaks. The memory diagram right before foo returns would look like:
The memory allocated on the heap and pointed to by p1 and p2 was freed
Note that p1 and p2 still exist, but they no longer point to valid memory. Accessing them would result in undefined behavior - we don’t know what might be at 0x40 or 0x80 anymore. The program below tries to use p1 and p2 after the memory they point at has been freed. It may print out garbage values or crash.
Listing 22.5.1.

Insight 22.5.1.

delete does not delete a pointer. It only tells the operating system that the memory the pointer was pointing to can be reused. From that point on, trying to use the pointer will lead to undefined behavior.
To help make sure we do not use a pointer after the memory it points at has been freed, it is good practice to set the pointer to nullptr. This way, we can write code to check if the pointer is null, and, even if we accidentally try to use it, we are guaranteed that the program will crash instead of doing something unpredictable. We can do this in our program with something like:
...
delete p1;
delete p2;

p1 = nullptr;
p2 = nullptr;
// --end of foo()--
...
Now at the end of the foo() function, the memory layout looks like this:
Setting pointers to nullptr ensures they don’t point at deallocated memory.
You have attempted of activities on this page.