|
| Tuesday, September 19, 2006 |
| Dynamic Memory and Pointers |
The Stack
- Function calls create space on an internal data
structure called "the stack" for all local variables in the function, all formal
parameters (which are just local variables), all objects (which are also local
variables inside the function), and for the return value if it is not void.
- The stack is really just an area
of memory, with a pointer that keeps track where the "top" of the stack is.
As data is put "on" the stack, the pointer is moved upward. As data is
removed, the pointer is moved downward. The "removed" data is just left
there, and written over when new data is put on the stack.
- If a function calls another
function, that function's space is created on top of the calling function's
space. The arguments in the calling function are copied into the formal
parameter's space on the stack. Then the calling function goes dormant
until the called function returns.
- If a formal parameter is an object, then the entire object
is copied using the copy constructor, if one exists. Otherwise the object
is copied bit by bit making an exact clone of the object just as it was the
instant the function was called.
- If a formal parameter is a pointer, only the pointer is
copied, not the value it points at.
- If the formal is a reference, a "reference" is copied.
This is really just a hidden pointer where the compiler adds the necessary code
to replace the asterisks in the function.
- The
function uses the stack space to store variable values while it executes.
- When the
called function returns, the stack space it was using is "taken off" the stack.
The function's local variables are now unavailable and may be overwritten at any
time.
- The return value of the function is copied to the proper
location in the calling function's area of the stack.
Dynamic memory
-
The rest of memory, not used by the stack, is called the heap.
- The heap is not organized in a logical way like the stack.
- The heap is available for your use to create locations for
data and objects. You can create a data location for any data type you
want, including any class you create. In other words, you can create
objects on the heap.
- Most programmers access data or objects on the heap using
pointers. For an alternative using references see below.
- The objects you create will remain until you explicitly
delete them and return the memory to the available heap. They will not be
affected by function calls and returns.
- You create an object on the heap with the keyword new and a
constructor call.
Class*
object = new Class; //default constructor
or
Class*
object = new Class(arg);
- Then use your object using the arrow operator:
object->someFunction();
- You can pass your dynamic variable or object to a function. Since it is already a pointer, you don't need to use the address-of operator. You would have to dereference it to pass by value.
// pass by
reference using pointers
void afunction(Class* object);
// prototype
afunction(object);
// call
// pass by
value
void afunction(Class object); // prototype
afunction(*object);
// call
- You can create arrays on the heap. If it is an array of objects, then
the default constructor will be automatically called for each object in the
array.
Class*
object = new Class[10];
- Always destroy your object when you are finished, returning
the memory to the available heap.
delete object;
Dynamic
arrays
-
You can create an array on the heap using dynamic memory. If it is an array of objects, then
the default constructor will be automatically called for each object in the
array.
Class*
object = new Class[10];
- Dynamic arrays solve the problem of having to determine the
size of the array when you write the program. The size of an array on the
heap can be any variable or expression, determined at run time.
int
size;
cin >> size;
Class*
objectarray = new Class[size];
- You then use the array just as you would any array using
the [ ] operators. You don't need the arrow operator because array names
are always pointers. There is no difference in syntax between an array on
the stack and a dynamic array on the heap.
object[i].someFunction();
- Passing your dynamic array to a function is exactly the same as arrays on the stack. Dynamic arrays are pointers just like fixed arrays on the stack..
// passing a
dynamic array to a function
// arrays always pass by reference
void afunction(Class objectarray[]);
// prototype
afunction(objectarray);
// call
- Since you are dealing with pointers, if you create an array on the heap, the compiler does not know if your pointer is a single object or an array. Remember pointers to single objects and arrays are exactly the same thing. Delete an array with the special syntax that tells the compiler this pointer is an array. The compiler does not need the size of the array.
delete [] object;
- The following program creates an array of four Rectangles and prints the areas:
#include <iostream>
#include "Rectangle.h"
using namespace std;
int
main()
{
Rectangle*
boxes = new Rectangle[4];
for (int i = 0; i < 4; i++)
{
boxes[i].setWidth(i);
boxes[i].setHeight(i);
}
for (int i = 0; i < 4; i++)
{
cout <<
"The area of boxes[" << i <<
"], of width " <<
boxes[i].getWidth() << " and height " <<
boxes[i].getHeight() << " is " <<
boxes[i].calcArea() << "." << endl;
}
delete[] boxes;
return 0;
}
Definitions the .h file:
class Rectangle
{
public:Rectangle();
void setWidth(int);
void setHeight(int);
int getWidth();
int getHeight();
int calcArea();private:
int width, height;
};Code in the .C file:
Rectangle::Rectangle()
{
height = 0;
width = 0;
}
void Rectangle::setWidth(int w)
{
width = w;
}
void Rectangle::setHeight(int h)
{
height = h;
}
int Rectangle::getWidth()
{
return width;
}
int Rectangle::getHeight()
{
return height;
}
int Rectangle::calcArea()
{
return height * width;
}
Output:The area of boxes[0], of width 0 and height 0 is 0.
The area of boxes[1], of width 1 and height 1 is 1.
The area of boxes[2], of width 2 and height 2 is 4.
The area of boxes[3], of width 3 and height 3 is 9.
Using references instead of pointers (extra material not in Deitel book. If this confuses you, just stick with pointers.)
- You can also access data or objects on the heap using references.
- Once created, the reference variable looks and acts just like a stack variable, not a pointer.
- You must dereference the pointer created by new, and use it to initialize a reference.
- Once a reference is created, it cannot be reassigned like a pointer. It will always represent only that one object on the heap.
Class& object = *new Class; //default constructor
or
Class& object = *new Class(arg);
- Now you use your object using the dot operator, just like an object created on the stack:object.someFunction();
- Your reference variable is treated exactly like a normal variable, including the way it is passed as a parameter.
// pass by value
void afunction(Class object); // prototype
afunction(object); // call
// pass by value using const reference
void afunction(const Class& object); // prototype
afunction(object); // call
// pass by reference using a pointer
void afunction(Class* object); // prototype
afunction(&object); // call
// pass by reference using a reference
void afunction(Class& object); // prototype
afunction(object); // call
- Notice in the last example, you are passing a reference to a reference. This will work fine with no change of syntax from passing any normal variable to a reference parameter.
- Always destroy your object when you are finished, returning the memory to the available heap.
- To delete the object, you must turn the reference back into a pointer by taking its address with the ampersand operator.
delete &object;
Readings
Deitel and Deitel
Fifth Edition,
Sections 8.1, 8.2, 8.3, 8.4, 8.7, 8.8, and 8.9 .
| Back to Csc 125 Computer Science II, Programming in C++ |
| Scott Badman Office: B132 Phone: 353-2250 sbadman@parkland.edu |
Parkland College, 2400 W. Bradley Avenue, Champaign, IL 61821 |