Further Pointer Pathology with Heap Memory
The result of applying delete to a pointer that holds the address of a valid object in the heap is to change the status of that heap memory from "in use" to "available." After delete has been applied to a pointer, the state of that pointer itself is undefined. The pointer may or may not still store the address of that deleted memory, so a second application of delete to the same pointer may cause run-time problemspossibly heap corruption.
In general, the compiler cannot detect attempts to apply delete repeatedly to the same object, especially if that piece of memory (or a part thereof) has since been reallocated. To help avoid the very undesirable consequences of a repeated delete, it is good practice to assign NULL to a pointer immediately after it has been deleted.
If delete is applied to a null pointer, there is no action and no error.
Applying delete to a non-null pointer that was not returned by new produces undefined results. In general, the compiler will not be able to determine whether the pointer was or was not returned by new, so undefined run-time behavior can result. Bottom line: It is the programmer's responsibility to use delete correctly.
One of the richest sources of run-time errors is the production of memory leaks. A memory leak is produced when a program causes memory to be allocated and then loses track of that memory so that it can neither be accessed nor deleted. An object that is not properly deleted will occupy memory until the process terminates.
Some programs (e.g., operating systems) stay active for a long time. Suppose such a program contains a frequently executed routine that produces a memory leak each time it is run. The heap will gradually become perforated with blocks of inaccessable, undeleted memory. At some point a routine that needs a substantial amount of contiguous dynamic memory may have its request denied. If the program was not expecting an event like that, it may not be able to continue.
The operators new and delete give the C++ programmer increased power as well as increased responsibility.
Here is some sample code that illustrates a memory leak. After defining a couple of pointers, memory will look a little like Figure 22.1.
int* ip = new int; // allocate space for an int int* jp = new int(13); // allocate and initialize cout << ip << ' ' << jp << endl;
Figure 22.1. Initial values of memory
Now we add one more line of code.
jp = new int(3); // reassign the pointer - MEMORY LEAK!!
After executing the single line above, our memory looks like Figure 22.2.
Figure 22.2. Memory after leak
Example 22.3. src/pointers/pathology/pathologydemo1.cpp
#include using namespace std; int main() { int* jp = new int(13); <-- 1 cout << jp << ' ' << *jp << endl; delete jp; delete jp; <-- 2 jp = new int(3); <-- 3 cout << jp << ' ' << *jp << endl; jp = new int(10); <-- 4 cout << jp << ' ' << *jp << endl; int* kp = new int(17); cout << kp << ' ' << *kp << endl; return 0; } Output: OOP> g++ pathologydemo1.cpp OOP> ./a.out 0x8049e08 13 0x8049e08 3 0x8049e08 10 Segmentation fault OOP> (1)allocate and initialize (2)error: pointer already deleted (3)Reassign the pointerMEMORY LEAK! (4)Reassign the pointerMEMORY LEAK! |
In Example 22.3, we deleted the pointer jp twice. The second deletion is a serious error but the compiler did not catch it. That error corrupted the heap, made any further memory allocation impossible, and made the behavior of the program beyond that point undefined. For example, notice that when we attempted to produce a memory leak by reassigning the pointer jp, we did not get any new memory. When we attempted to introduce another pointer variable we got a segmentation fault. This is all undefined behavior and may be different on another platform or with another compiler.