Let's do an example. We have the following construction:
class Animal
{
public:
virtual string sound() = 0;
virtual ~Animal() {}
};
class Pig: public Animal
{
public:
string sound() override { return "Oink!"; }
};
class Cow : public Animal
{
public:
int milk; // made this public for easy code, but probably should be encapsulated
Cow() : milk(5) {}
string sound() override { return "M" + string(milk, 'o') + "!"; } // a Moo! with milk times many Os
};
What we now want to do is to store a list of animals. We can't use vector<Animal>
because that would contain instances of the class Animal
, which is purely abstract - Animal::sound
is not defined.
However, we can use a vector of pointers:
vector<Animal*> animals;
animals.push_back(new Cow);
animals.push_back(new Pig);
animals.push_back(new Cow);
for(Animal* animal : animals)
{
cout << animal->sound() << endl;
}
So far, so good. But now take a look at the class Cow
, there is the member milk
which has an influence on the output. But how do we access it? We know that the first and third entries in animals
are of type Cow
, but can we use it? Let's try:
animals[0]->milk = 3;
This yields:
error: 'class Animal' has no member named 'milk'
However, we can do this:
Cow* cow = (Cow*) animals[0];
cow->milk = 3;
for(Animal* animal : animals)
{
cout << animal->sound() << endl;
}
What we done here is to create a pointer to Cow
from a pointer to Animal
of which we knew that it was actually pointing to an object of type Cow
.
Note that this is risky - here, we knew that the first entry in the vector is of that type, but in general, you don't. Therefore, I recommend that you use safe casting, that is especially dynamic_cast
. If you come to understood pointer casting and feel safe in that topic, read some tutorial on how to use dynamic_cast
.
Right now, we have cast the base class to the derived class, but we can also do the opposite:
Cow* cow = new Cow;
cow->milk = 7;
animals.push_back((Animal*) cow);
for(Animal* animal : animals)
{
cout << animal->sound() << endl;
}
I assembled all of this into http://www.cpp.sh/6i6l4 if you want to see it in work.
Hope that this gives you a better understanding of what we need this for. Storing a list with objects of different types but a common base class is quite usual, and likewise is pointer to unknown subtypes. If you want more practical examples, ask. Thought about providing one, but don't want to overwhelm you for the start.
(Lastly, we need to clean up our memory, as we put our variables in the heap:
for(Animal* animal : animals)
{
delete animal;
}
animals.clear();
if we wouldn't do it, we'd have a memory leak. Better would have been to use smart pointers like shared_ptr
- again here the recommendation to read into that when you feel safe in the base topic.)