Take the following C++ for example.
vector listAnimal;
class Fish : Animal ...
class Mammal : Animal ...
class Bird : Animal ...
A container only stores elements of a fixed type, you want a pointer to an object.
#include <memory>
#include <vector>
std::vector<std::unique_ptr<Animal>> animal_list;
animal_list.emplace_back(new Fish);
animal_list.emplace_back(new Mammal);
animal_list.emplace_back(new Bird );
Store Animal
type in vector will cause object slice
when push derived types into listAnimal.
vector<Animal> listAnimal;
listAnimal.push_back(Fish); // Fish is sliced to Animal, no fish for you.
Edit:
To know what type a derived animal is, you could store it in a member
Enum AnimalType
{
FISH,
MAMAL,
BIRD
};
class Animal
{
public:
Animal(AnimalType animal_type) : type(animal_type) {}
AnimalType GetType() const { return type; }
private:
AnimalType type;
};
I typically create a pure virtual function that each derived class implements to tell you its identity. Example:
enum AnimalType
{
Fish = 0,
Mammal,
Bird
}
class Animal
{
virtual AnimalType GetType() const = 0;
}
...
AnimalType Bird::GetType()
{
return Bird;
}
Then you can do something like this:
if (animal.GetType() == Bird)
{
// ...
}
You shouldn't need to know what type of sub-class you're dealing with. You're not doing polymorphism right if you need to check the type of class you're dealing with. The whole point of polymorphism is to reduce if's and make your code a lot more flexible.
There are some cases where you need to know, and you can use RTTI for that. However I recommend not to, especially if you require a lot of performance (such as games or graphics programs).
Use the typeid
operator to get information about a class, and to determine if a class is a specific type.
For example:
Animal* animal1 = new Cat;
if(typeid(animal1) == typeid(Cat))
{
cout << "animal1 is a: " << typeid(Cat).name();
}
Then use a static_cast
to cast it down the hierarchy.
Animal* animal1 = new Cat;
if(typeid(animal1) == typeid(Cat))
{
Cat* cat = static_cast<Cat*>(animal1);
cat->scratchTheLivingHellOutOfYou();
}
Alternatively you can use a dynamic_cast
which is much safer, but much slower than a typeid/static_cast. Like so:
Animal* animal1 = new Cat;
if(Cat* cat = dynamic_cast<Cat*>(animal1)
{
cat->scratchTheLivingHellOutOfYou();
}
dynamic_cast
is slower simply because it has to do a little extra work than just testing if it's a specific type and casting. i.e. dyanmic_cast
is not equivalent to typeid/static_cast
, but it almost is.
Imagine a hierarchy further than 2 levels deep, for example:
class Animal { /* ... */ }; // Base
class Cat : public Animal { /* ... */ }; // 2nd level
class Tiger : public Cat { /* ... */ }; // 3rd level
Let's say that in the Cat class, a method specific to all Cats is called: scratchTheLivingHellOutOfYou()
. Let's also say that: I have a list of Animals and I want to call scratchTheLivingHellOutOfYou()
for every Cat in the list (this includes classes that derive from the class Cat). If the typeid
operator and static_cast
is used, this would not achieve what is required, since typeid
only checks for the current type and does not care about the hierarchy. For this, you have to use a dynamic_cast
, since it will check if a class is derived from a base-class, and then cast up/down the hierarchy accordingly.
You can see this simple example, in C++, here. Here is the output of the program:
USING TYPEID
*scratch*
meoyawnn!
RAWR
USING DYNAMIC_CAST
*scratch*
meoyawnn!
*scratch*
RAWR
Therefore, you can clearly see that dynamic_cast
does a lot more work than a simple typeid
and static_cast
. Since dynamic_cast
looks up the hierarchy to see if it is a specific type. Simply put... dynamic_cast
can cast up and down the hierarchy. Whereas a typeid
and static_cast
can only cast down the hierarchy to a specific type.
I thought I'd mention that if dynamic_cast
fails it will return a NULL pointer, or throw an exception if you're using it with references.
dynamic_cast
should only be used if you're not sure the object will be the type you're converting to. If you, as a programmer, know whatever you're casting is 100% going to be that type then use static_cast
, e.g. if you know animal1 is going to be a Cat
then static_cast
is more appropriate.