CS 70

What About Arrays?

  • LHS Cow speaking

    So far today, we've only been looking at individual values. But we should also make sure we can reason about object lifetimes for arrays of values.

  • Hedgehog speaking

    That seems exponentially more complicated.

  • LHS Cow speaking

    It's not so bad!

Arrays are exactly the same as individual values; each phase of life just happens N times for an array of N values!

Allocation Phase

Allocation happens at the same time as for individual objects, but space for all N values is allocated at once.

  • Hedgehog speaking

    So Hedgehog array[5] (see here) will allocate space for five Hedgehogs on the stack?

  • LHS Cow speaking

    Exactly!

Initialization Phase

Initialization follows the same timing rules as individual objects, but will happen N times: once for each element of the array (in order).

  • RHS Cow speaking

    For a default-initialized array of N instances of a class, the default constructor will be called N times!

Use Phase

Use happens in the same way as for individual objects, for each individual element of the array.

Destruction Phase

Destruction follows the same timing rules as individual objects, but will happen N times: once for each element of the array.

For an array of N instances of a class, the destructor will be called N times!

  • LHS Cow speaking

    Mirroring the “destruction is the inverse of construction” idea, elements are destroyed in reverse order (i.e., the last element is destroyed first; the first element is destroyed last).

Deallocation Phase

  • Cat speaking

    I think I'm seeing a pattern here. Let me guess:
    Deallocation follows the same timing rules as individual objects, but space for all N values is deallocated at once.

  • LHS Cow speaking

    Yup!

Practice Problem

Suppose we have these member functions in our Cow class,

Cow::Cow() {
    cout << "Cow default constructor called" << endl;
}

Cow::~Cow() {
    cout << "Cow destructor called" << endl;
}

…and the following program that uses our Cow class:

int main() {
    Cow herd[5];
    Cow bessie;
    Cow mabel{3, 13};
}

When this program runs, how many times will the Cow default constructor called message be printed?

When this program runs, how many times will the Cow destructor called message be printed?

  • Horse speaking

    Hay! In a previous lesson, we disabled the default constructor for Cow. Would this code still work then?

  • LHS Cow speaking

    Nope! If the default constructor is disabled, then the array declaration Cow herd[5]; would be a compile-time error, since it requires the default constructor to initialize each element of the array.

  • Pig speaking

    So we can't have arrays of Cows at all if we disable the default constructor? I want MORE cows!

  • LHS Cow speaking

    You can still have arrays of Cows, but you have to explicitly initialize each element of the array. Let's look at an example.

Arrays of Non-Default Objects

Suppose we have an Egg class that only has a constructor that takes a std::string for the egg's color and an int for the egg's size, with no default constructor.

We can make one egg like this,

Egg myEgg{"white", 3};

But if we try to make an array of eggs like this,

Egg carton[6];

the code won't compile, because the default constructor is disabled but the array elements would need to be default initialized. We can make an array of eggs if we explicitly initialize each element, like

Egg carton[6] = {
    {"white", 3},
    {"brown", 4},
    {"beige", 2},
    {"eggshell", 3},
    {"speckled", 5},
    {"light-pink", 1}
};

We can think of the construction process a bit like nesting dolls. The outer braces are for constructing the array, and the inner braces are for constructing each individual Egg in the array.

  • Duck speaking

    It seems odd to me that we don't say Egg carton[6] = {Egg{"white", 3}, Egg{"brown", 4}, ...};. Why don't we have to say Egg for each element?

  • LHS Cow speaking

    Good question! The compiler knows that each element of the array is an Egg, so it can infer that from the context.

  • RHS Cow speaking

    Technically, you can write it that way if you want to, but it would make your code more verbose than it needs to be. When we say Egg explicitly, we're actually asking the compiler to make an egg, and then copy that egg into the array element. That's a bit wasteful.

  • Bjarne speaking

    Technically, it isn't. People wrote this kind of code enough that we ended up requiring that the compiler optimize away the copy in this case.

  • LHS Cow speaking

    But it's still more idiomatic to just leave it out. (And, uh, thanks Bjarne for adding more irregularity to the language. Super helpful.)

  • Bjarne speaking

    My questionable past decisions are part of our heritage!

For a bit more practice with array, check out this code:

Look it over and run it. Be sure to either expand the output pane at the bottom or scroll it back up to see all the output.

Looking at the way the array gets created and destroyed, what thing that we mention earlier are you reminded of?

You're also free to play with the code if you like. If you did, feel free to share anything you found interesting!

(When logged in, completion status appears here.)