The Five Stages of an Object's Life
What do you mean by “Object”. Like from a class?
Actually, in C++ terminology, “object” just means anything that has a type and lives in memory, whether something like an
int
or adouble
, or an instance of a class.So every variable refers to an object?
That's right!
Every object (whether it is an instance of a class, or a primitive type like int
or double
) goes through the same five stages over the course of its life: allocation, initialization, use, destruction, and deallocation. Understanding when those "life events" happen, and what happens to an object in each stage, will help us reason about the correctness of C++ code.
For the purposes of this modeling, we will consider a function's local variables to include:
- all of the function's parameters
- all variables declared in the body of the function
The five phases of an object's lifetime are:
- Allocation: Acquire memory for the object
- Initialization: Get the object into a useable starting state
- Use: Access the object (for printing, passing to a function, changing its value, etc.)
- Destruction: Clean up the memory for the object
- Deallocation: Give the object's memory back
An Analogy for Object Lifetime
We can think about the five stages of an object's lifetime using the analogy of a physical building.
A Worked Example
We can use our knowledge of object lifetimes to trace the state of memory for a C++ program:
int triple(int multiplier)
{
int product = 3 * multiplier;
return product;
}
int main()
{
int myInt = 0;
int myConstant = 14;
myInt = triple(myConstant);
cout << myInt << endl;
return 0;
}
Hay! Prof. Medero drew an arrow when she was showing data moving from one place to another. Do I need to draw an arrow like that every time I move data?
No, actually that wasn't part of the diagram at all, that was just to show you what was happening in the program and where the number came from.
Sorry for the confusion! Please don't draw arrows like that in your diagrams! In fact, let's make it clear what the rules really are for drawing memory diagrams…
Memory Diagram Rules
Drawing pictures really helps us understand what's going on in memory, but we need to make sure we draw the right picture that gives us a good mental model for what happens when a C++ program runs. For that reason, we have specific rules for how to draw a memory diagram in CS 70.
Meh, I'm not a fan of rules. And it seems like a lot of effort to go to when I can usually just keep things straight in my head.
Memory diagrams like these are going to be suuuuuper helpful, as our C++ programs get more complicated.
Drawing them methodically is key, even for simple programs.
Uh… But how do I know what to draw and when?
You can follow a simple set of rules at each step of your program to keep your diagram updated.
As with anything else, it becomes easier with practice.
Here are the rules we need now. We'll add more as we go.
- A program starts at the
main
function. - When you reach the opening curly brace of a function, all local variables in that function are allocated.
- Draw a slot for every local variable (including function arguments and variables declared inside the function).
- Label each slot with its type and its memory address (e.g., for the stack that would be S1, S2, etc.)
- Label that collection of slots as the function's stack frame.
- When you reach a variable declaration, that variable is initialized.
- Label the variable's slot with its name, to indicate that the name can be used in the program.
- If you know the variable's initial value, put that value in the variable's memory slot.
- If you don't know the value (for instance, if the value is undefined), then leave it blank.
- When a variable's value changes:
- Cross out the old value in the variable's slot, and write the new value.
- When you reach a closing curly brace, all variables declared in the block being closed are destroyed.
- Cross out the name of each destroyed variable, indicating that the name can no longer be used in the program.
- When you reach the closing curly brace of a function, all local variables in that function are deallocated.
- For each local variable, cross out its type, indicating that this space is available to be allocated for some other use.
- Cross out the label for the stack frame, indicating that the previous frame is now the current frame.
You try it!
Trace the state of memory for the following C++ code:
int cube(int base)
{
int outcome = base * base;
outcome = outcome * base;
return outcome;
}
int main()
{
int myInt = 0;
int myConstant = 3;
myInt = cube(myConstant);
cout << myInt << endl;
return 0;
}
Once you've had a go, answer the following question…
Let's Do It Together
Let's go over that exercise together.
(When logged in, completion status appears here.)