Section 2 - Stack vs. Heap Memory
3B.2.1 Stack and Heap
There are two places in memory where your variables and objects all live: stack and heap. We want to understand which variable objects live in the heap and which live in the stack. In this section, the term stack will mean the program stack. There is a separate meaning for the term stack that is used later in the lesson.
3B.2.2 The Program Stack
Recall that we said that local variables are only valid in the method in which they are defined. Variables defined in any method (function) are created in the stack.
int main() { float x, y; char name[50]; }
All three variables, x, y and the char array name[] are local to main and therefore live in the program stack, or stack. If we call a method, func() from inside main(), then all of func()'s local variables are added to the program stack, so they, too, live in the stack. When func() returns to main(), func()'s locals are all destroyed (removed from the program stack).
There is an exception to this rule, namely static variables. When variables are declared static, their storage is done at global scope and is not part of the program stack.
In ordinary programs with no pointers or dynamic memory allocation, all variables live in the stack.
3B.2.3 The Free Heap
There is also an area of memory called the free heap or just heap. Whenever we use a pointer with new to create an object dynamically, the object is created in the heap and the pointer points to it.
float *p; p = new float[50];
The memory that p points to, the 50 floats, are allocated in the heap. The variable p, itself, is not part of the heap, but lives in the stack. Don't get confused about this. A variable, p, can live in the stack yet point to memory that lives in the heap. (Can you think of a situation in which a pointer points to memory that is not in the heap, but in the stack?)
Furthermore, when we issue the command:
delete[] p;
we are not deleting the variable p. We are deleting the 50 floats that p points to.
3B.2.4 Cleaning Up Memory
When we have no pointers and no dynamic allocation, the C++ run-time routines take care of managing memory for us. They maintain the variables on the stack. As we enter more functions, the stack is increased to accommodate the local variables in the various methods. When the methods return, the stack deallocates memory and removes local variables that are no longer "in-scope", i.e., they were in a method that has returned to the client.
However, if we use new to allocate memory dynamically, we get no help at all from our compiler. We have to delete the memory ourselves. This is why we have to be careful when using new and delete. Memory leaks can occur if we do not use them correctly, which can bring our program to a crawl.
3B.2.5 Destructors
We've learned that destructors are the opposite, so-to-speak, of constructors, but we have never used them. The main job of a destructor is to clean up memory for us. It's not always the only job, but in C++, that's its primary purpose. Now you see why we haven't used destructors yet: We have not had any heap memory in our classes to clean up. I just said that C++ takes care of stack memory, and all of our member data has been ordinary stack variables, so there's been no need to "destruct" anything.
All that is about to change. We will create an example class, Galaxy, in the next section that uses dynamic allocation inside itself. This will produce memory on the heap when the Galaxy objects are constructed, requiring that we de-allocate (delete) the memory in the Galaxy destructor.
Let's blast off.