What About Arrays?
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.
That seems exponentially more complicated.
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.
So
Hedgehog array[5]
(see here) will allocate space for fiveHedgehog
s on the stack?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).
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!
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
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.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};
}
Hay! In a previous lesson, we disabled the default constructor for
Cow
. Would this code still work then?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.So we can't have arrays of
Cow
s at all if we disable the default constructor? I want MORE cows!You can still have arrays of
Cow
s, 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.
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 sayEgg
for each element?Good question! The compiler knows that each element of the array is an
Egg
, so it can infer that from the context.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.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.
But it's still more idiomatic to just leave it out. (And, uh, thanks Bjarne for adding more irregularity to the language. Super helpful.)
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.
(When logged in, completion status appears here.)