Polymorphic Storage
Didn't we say that a big reason for doing this was so we could have a
std::vectorof animals for our Zoo?
We did.
Let's do it. MORE animals!
Open up the code from before:
- Open Code in OnlineGDB
- Click the button on the top left, scroll down to
main, and then replace the code in the body ofmainwith the following code:
- Click the button on the top left, scroll down to
auto cowNames = {"Bessie", "Mabel", "Daisy"};
auto raptorNames = {"Peri", "Rex", "Talon"};
std::vector<Animal> zoo;
for (auto name : cowNames) {
Cow tempCow(name);
zoo.push_back(tempCow);
}
for (auto name : raptorNames) {
Raptor tempRaptor(name);
zoo.push_back(tempRaptor);
}
for (Animal& animal : zoo) {
engageWith(animal);
}
The code makes three Cows and three Raptors, then adds them to our zoo vector, and then calls engageWith() on each of them (which has them speak() and feed()).
You should have seen something like
Bessie makes nonspecific animal noises
Bessie eats
Mabel makes nonspecific animal noises
Mabel eats
Daisy makes nonspecific animal noises
Daisy eats
Peri makes nonspecific animal noises
Peri eats
Rex makes nonspecific animal noises
Rex eats
Talon makes nonspecific animal noises
Talon eats
Generic Animals
Hay! What did we do wrong? Hadn't we solved this already?
Oh, no, I think I know what happened—we made
CowsandRaptorsand then turned them into these zombie-like generic animals.
Exactly.
Our std::vector only stores base-class Animal objects. Every time we call push_back with a Cow or a Raptor, we only copy the base Animal part of the object into the vector.
Okay, but I'm not seeing the right way to store an
Animalobject where it could actually really be aCowor aRaptor.
You can't.
What?
Woah, what?
Huh?
Yes, it's pretty much impossible. Because we don't know how many extra data members a derived class might add, we don't even know how much space would be needed per element of the array.
But there is a way… a slightly indirect way.
Meh. It's going to be pointers, isn't it?
Oh, no.
Storing Pointers to Objects
When we're using derived classes, we often end up needing to use pointers.
We can switch out the code above with this code that uses a vector of pointers to animals:
auto cowNames = {"Bessie", "Mabel", "Daisy"};
auto raptorNames = {"Peri", "Rex", "Talon"};
std::vector<Animal*> zoo;
for (auto name : cowNames) {
zoo.push_back(new Cow{name});
}
for (auto name : raptorNames) {
zoo.push_back(new Raptor{name});
}
for (Animal* animal : zoo) {
engageWith(*animal);
}
while (!zoo.empty()) {
delete zoo.back();
zoo.pop_back();
}
Now run this version.
You should have seen
Bessie says: Mooooo
Bessie eats
Mabel says: Mooooo
Mabel eats
Daisy says: Mooooo
Daisy eats
Peri says: Rawrrr
Peri eats
Rex says: Rawrrr
Rex eats
Talon says: Rawrrr
Talon eats
So our code works! We just need to use pointers.
I tried to escape templates and I ended up with pointers. Oh, joy.
It's not so bad really.
I never had these problems in Java.
Remember, Java is pretty much all pointers all the time. Every time you have an object in Java, it's really a pointer to that object. (They call it a “reference” to try to soften the blow.)
What I want to know is why we were ever able to make these bland generic
Animalobjects in the first place—we only wantCows andRaptors.
Okay, let's see if we can sort out that aspect of things…
(When logged in, completion status appears here.)