Possible Duplicate:
Learning C++: polymorphism and slicing
This is building off a question I asked before.
The classes look like this:
class Enemy
{
public:
void sayHere()
{
cout<<"Here"<<endl;
}
virtual void attack()
{
}
};
class Monster: public Enemy
{
public:
void attack()
{
cout<<"RAWR"<<endl;
}
};
class Ninja: public Enemy
{
public:
void attack()
{
cout<<"Hiya!"<<endl;
}
};
I am new to C++ and I’m confused as to why this will only work with pointers (both Ninja and monster are derived from Enemy):
int main()
{
Ninja ninja;
Monster monster;
Enemy *enemies[2];
enemies[0] = &monster;
enemies[1] = &ninja;
for (int i = 0; i < 2; i++)
{
enemies[i]->attack();
}
return 0;
}
Why can’t I instead do this?:
int main()
{
Ninja ninja;
Monster monster;
Enemy enemies[2];
enemies[0] = monster;
enemies[1] = ninja;
for (int i = 0; i < 2; i++)
{
enemies[i].attack();
}
return 0;
}
This is a great question that hits at the heart of some of the trickier points of C++ inheritance. The confusion arises because of the difference between static types and dynamic types, as well as the way that C++ allocates storage for objects.
To begin, let’s discuss the difference between static and dynamic types. Every object in C++ has a static type, which is the type of the object that is described in the source code. For example, if you try writing
Then the static type of
bisBase*, since in the source code that’s the type you declared for it. Similarly, if you writethe static type of
myBasesisBase[5], an array of fiveBases.The dynamic type of an object is the type that the object actually has at runtime. For example, if you write something like
Then the dynamic type of
bisDerived*, since it’s actually pointing at aDerivedobject.The distinction between static and dynamic types is important in C++ for two reasons:
Let’s address each of these in turn.
First, one of the problems with the second version of the code is that you do the following:
Let’s trace through what happens here. This first creates a new
NinjaandMonsterobject, then creates an array ofEnemyobjects, and finally assigns theenemiesarray the values ofninjaandmonster.The problem with this code is that when you write
The static type of the lhs is
Enemyand the static type of the rhs isMonster. When determining how to do an assignment, C++ only looks at the static types of the objects, never the dynamic types. This means that becauseenemies[0]is statically typed as anEnemy, it has to hold something precisely of typeEnemy, never any derived type. This means that when you do the above assignment, C++ interprets this to mean “take themonsterobject, identify just the part of it that’s anEnemy, then copy that part over intoenemies[0].” In other words, although aMonsteris anEnemywith some extra additions, only theEnemypart ofMonsterwill be copied over intoenemies[0]with this line of code. This is called slicing, since you’re slicing off part of the object and leaving behind just theEnemybase portion.In the first piece of code that you posted, you have this:
This is perfectly safe, because in this line of code:
The lhs has static type
Enemy*and the rhs has typeMonster*. C++ legally allows you to convert a pointer to a derived type into a pointer to a base type without any problems. As a result, the rhsmonsterpointer can be converted losslessly into the lhs typeEnemy*, and so the top of the object isn’t sliced off.More generally, when assigning derived objects to base objects, you risk slicing the object. It is always safer and more preferable to store a pointer to the derived object in a pointer to a base object type, because no slicing will be performed.
There’s a second point here as well. In C++, whenever you invoke a virtual function, the function is only called on the dynamic type of the object (the type of the object that the object really is at runtime) if the receiver is a pointer or reference type. That is, if you have the original code:
And write
then because
enemies[0]has static typeEnemy, the compiler won’t use dynamic dispatch to determine which version of theattackfunction to call. The reason for this is that if the static type of the object isEnemy, it always refers to anEnemyat runtime and nothing else. However, in the second version of the code:When you write
Then because
enemies[0]has static typeEnemy*, it can point at either anEnemyor a subtype ofEnemy. Consequently, C++ dispatches the function to the dynamic type of the object.Hope this helps!