Initializing Heap Memory in Constructors
On the last page we decided that we should change our
Barn
class.Our new version can store any number of
Cow
s in an array on the heap!
In particular, we decided to change residentCow_
—which was a Cow
—to residentCows_
, which is a Cow*
that will point to an array of Cow
objects on the heap:
class Barn {
public:
Barn();
Barn(size_t numCows);
void feed();
// Disable copying and assignment
Barn(const Barn& other) = delete;
Barn& operator=(const Barn& rhs) = delete;
private:
size_t cowCapacity_;
Cow* residentCows_; // Points to an array of Cow objects on the heap
};
Let's declare a local variable, Barn barney{4};
.
Initializing Heap Memory
If an instance of a class needs to use heap memory to store its values, then that memory will often be allocated in the constructor.
Remember that whenever possible, we want to initialize memory in the member-initialization list!
So we'll have to put a call to
new
inside the member-initialization list!
Let's focus on the following parameterized constructor for our Barn
class, which has been very slightly updated to change the name of the second data member from residentCow_
(which was a Cow
) to residentCows_
(which is a Cow*
):
// Note: This code is not quite right yet!
Barn::Barn(size_t numCows) : cowCapacity_{numCows}, residentCows_{"Bessie", 42}
{
// Nothing (else) to do here!
}
Hay, wait! If
residentCows_
is aCow*
, then we can't initialize it like it's aCow
, can we?!You're right! We need to update our member-initialization list to reflect the new type of
residentCows_
.
Dropping that code into our member-initialization list, we get
Barn::Barn(size_t numCows) : cowCapacity_{numCows}, residentCows_{new Cow[cowCapacity_]}
{
// Nothing (else) to do here!
}
You can explore the revised code by clicking the button below:
Hmm… It looks like we're using one data member (
cowCapacity_
) to initialize another data member (residentCows_
)…Yes! We're totally allowed to do that.
In fact, that's part of why C++ is so picky about us initializing variables in the same order they're listed in the class definition.
It helps us make sure that values we might use in a later initialization are ready for us to use.
With the changes we've made to the Barn
class, suppose we write (as we do in main
),
Barn jaysPlace{4};
What's Not to Like…?
Right now, a Barn
object always has cowCapacity_
Cow
objects. That's because it has an array of Cow
s, which is just that many Cow
s all together in memory.
So what you're saying is, we have some flexibility now, in that we can change the number of cows, but it's still the case that we end up locked into that number afterwards.
Right, and that's not great. It's also not great that they're all bland default cows.
Meh, I've always thought that arrays being a fixed size wasn't a great idea.
We'll have to come up with a way to add more flexibility to our fixed-size array.
By making things more complicated I expect?
A little bit.
Hay! There is another problem. When our
Barn
had a singleCow
, it was destroyed at the end. Now none of theCow
s get destroyed!Good eye. That's a bug right there, a memory leak, in fact. We'll fix it, but it'll take us a while to get there.
(When logged in, completion status appears here.)