CS 70

The Five Stages of an Object's Life

  • Duck speaking

    What do you mean by “Object”. Like from a class?

  • LHS Cow speaking

    Actually, in C++ terminology, “object” just means anything that has a type and lives in memory, whether something like an int or a double, or an instance of a class.

  • Cat speaking

    So every variable refers to an object?

  • LHS Cow speaking

    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;
}
  • Horse speaking

    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?

  • LHS Cow speaking

    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.

  • RHS Cow speaking

    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.

  • Goat speaking

    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.

  • LHS Cow speaking

    Memory diagrams like these are going to be suuuuuper helpful, as our C++ programs get more complicated.

  • RHS Cow speaking

    Drawing them methodically is key, even for simple programs.

  • Hedgehog speaking

    Uh… But how do I know what to draw and when?

  • LHS Cow speaking

    You can follow a simple set of rules at each step of your program to keep your diagram updated.

  • RHS Cow speaking

    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…

How did you do?

Consider these statements about the diagram you drew and decide which are true:

The value printed was

Let's Do It Together

  • LHS Cow speaking

    Let's go over that exercise together.

(When logged in, completion status appears here.)