Professional C__ - Marc Gregoire [364]
Memory allocation and management is a particularly error-prone area of C++ programming. To write high-quality C++ programs, professional C++ programmers need to understand how memory works behind the scenes. This chapter explores the ins and outs of memory management. You will learn about the pitfalls of dynamic memory and some techniques for avoiding and eliminating them.
WORKING WITH DYNAMIC MEMORY
When learning to program, dynamic memory is often the first major stumbling block that novice programmers face. Memory is a low-level component of the computer that unfortunately rears its head even in a high-level programming language like C++. Many programmers only understand enough about dynamic memory to get by. They shy away from data structures that use dynamic memory, or get their programs to work by trial and error.
There are two main advantages to using dynamic memory in your programs:
Dynamic memory can be shared between different objects and functions.
The size of dynamically-allocated memory can be determined at run time.
A solid understanding of how dynamic memory really works in C++ is essential to becoming a professional C++ programmer.
How to Picture Memory
Understanding dynamic memory is much easier if you have a mental model for what objects look like in memory. In this book, a unit of memory is shown as a box with a label next to it. The label indicates a variable name that corresponds to the memory. The data inside the box displays the current value of the memory.
For example, Figure 21-1 shows the state of memory after the following line is executed. The line should be in a function, so that i is a local variable:
FIGURE 21-1
int i = 7;
Since i is a local variable, it is allocated on the stack because it is declared as a simple type, not dynamically using the new keyword.
When you use the new keyword, memory is allocated on the heap. The following code creates a variable ptr on the stack, and then allocates memory on the heap to which ptr points.
int* ptr;
ptr = new int;
Figure 21-2 shows the state of memory after this code is executed. Notice that the variable ptr is still on the stack even though it points to memory on the heap. A pointer is just a variable and can live either on the stack or the heap, although this fact is easy to forget. Dynamic memory, however, is always allocated on the heap.
FIGURE 21-2
The next example shows that pointers can exist both on the stack and on the heap.
int** handle;
handle = new int*;
*handle = new int;
The preceding code first declares a pointer to a pointer to an integer as the variable handle. It then dynamically allocates enough memory to hold a pointer to an integer, storing the pointer to that new memory in handle. Next, that memory (*handle) is assigned a pointer to another section of dynamic memory that is big enough to hold the integer. Figure 21-3 shows the two levels of pointers with one pointer residing on the stack (handle) and the other residing on the heap (*handle).
FIGURE 21-3
Allocation and Deallocation
You should already be familiar with the basics of dynamic memory from earlier chapters in this book. To create space for a variable, you use the new keyword. To release that space for use by other parts of the program, you use the delete keyword. Of course, it wouldn’t be C++ if simple concepts such as new and delete didn’t have several variations and intricacies.
Using new and delete
When you want to allocate a block of memory, you call new with the type of the variable for which you need space. new returns a pointer to that memory, although it is up to you to store that pointer in a variable. If you ignore the return value of new, or if the pointer variable goes out of scope, the memory becomes orphaned because you no longer have a way to access it.
For example, the following code orphans enough memory to hold an int. Figure 21-4 shows the state of memory after the code is executed. When there are blocks of data on