[ fromfile: ptrpathology2.xml id: ptrpathologystorage ]
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 the deleted memory, so a second application of delete
to the same pointer may cause runtime problems, possibly 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 undesirable consequences of a repeated delete
it is good practice
to assign 0
or NULL
to a pointer immediately after it
has been deleted.
If delete
is applied to a null pointer, there is no error and no action.
Applying delete
to a non-null pointer that was not returned by new
produces undefined results.
In general, the compiler cannot determine whether the pointer was returned by new
so
undefined runtime behavior can result.
Bottom Line: It is the programmer's responsibility to use
delete
correctly.
One of the richest sources of runtime 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., servers, 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 gradually becomes 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 is not prepared for an event like that, it will abort.
The operators new
and delete
give the C++ programmer
increased power and increased responsibility.
Following is some sample code that illustrates a memory leak. After defining a couple of pointers, memory looks like Figure 21.1.
int* ip = new int; // allocate space for an int int* jp = new int(13); // allocate and initialize cout << ip << '\t' << jp << endl;
After executing the following line of code, memory looks like Figure 21.2.
jp = new int(3); // reassign the pointer - MEMORY LEAK!!
In Example 21.3, we delete the pointer jp
twice.
Example 21.3. src/pointers/pathology/pathologydemo1.cpp
#include <iostream> using namespace std; int main() { int* jp = new int(13); cout << jp << '\t' << *jp << endl; delete jp; delete jp; jp = new int(3); cout << jp << '\t' << *jp << endl; jp = new int(10); cout << jp << '\t' << *jp << endl; int* kp = new int(17); cout << kp << '\t' << *kp << endl; return 0; } Output: OOP> g++ pathologydemo1.cpp OOP> ./a.out 0x8049e08 13 0x8049e08 3 0x8049e08 10 Segmentation fault OOP>
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.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |