This question already has answers here:
Closed 11 years ago.
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
Base* b = new Derived;
Then the static type of b is Base*, since in the source code that's the type you declared for it. Similarly, if you write
Base myBases[5];
the static type of myBases is Base[5], an array of five Bases.
The dynamic type of an object is the type that the object actually has at runtime. For example, if you write something like
Base* b = new Derived;
Then the dynamic type of b is Derived*, since it's actually pointing at a Derived object.
The distinction between static and dynamic types is important in C++ for two reasons:
Assignments to objects are always based on the static type of the object, never the dynamic type.
Invocations of virtual functions only dispatch to the dynamic type if the static type is a pointer or reference.
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:
Ninja ninja;
Monster monster;
Enemy enemies[2];
enemies[0] = monster;
enemies[1] = ninja;
Let's trace through what happens here. This first creates a new Ninja and Monster object, then creates an array of Enemy objects, and finally assigns the enemies array the values of ninja and monster.
The problem with this code is that when you write
enemies[0] = monster;
The static type of the lhs is Enemy and the static type of the rhs is Monster. When determining how to do an assignment, C++ only looks at the static types of the objects, never the dynamic types. This means that because enemies[0] is statically typed as an Enemy, it has to hold something precisely of type Enemy, never any derived type. This means that when you do the above assignment, C++ interprets this to mean "take the monster object, identify just the part of it that's an Enemy, then copy that part over into enemies[0]." In other words, although a Monster is an Enemy with some extra additions, only the Enemy part of Monster will be copied over into enemies[0] with this line of code. This is called slicing, since you're slicing off part of the object and leaving behind just the Enemy base portion.
In the first piece of code that you posted, you have this:
Ninja ninja;
Monster monster;
Enemy *enemies[2];
enemies[0] = &monster;
enemies[1] = &ninja;
This is perfectly safe, because in this line of code:
enemies[0] = &monster;
The lhs has static type Enemy* and the rhs has type Monster*. 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 rhs monster pointer can be converted losslessly into the lhs type Enemy*, 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:
Ninja ninja;
Monster monster;
Enemy enemies[2];
enemies[0] = monster;
enemies[1] = ninja;
And write
enemies[0].attack();
then because enemies[0] has static type Enemy, the compiler won't use dynamic dispatch to determine which version of the attack function to call. The reason for this is that if the static type of the object is Enemy, it always refers to an Enemy at runtime and nothing else. However, in the second version of the code:
Ninja ninja;
Monster monster;
Enemy *enemies[2];
enemies[0] = &monster;
enemies[1] = &ninja;
When you write
enemies[0]->attack();
Then because enemies[0] has static type Enemy*, it can point at either an Enemy or a subtype of Enemy. Consequently, C++ dispatches the function to the dynamic type of the object.
Hope this helps!
Without pointers, your enemies[] array represents a space on the stack sufficient to store two "Enemy" objects-- which means storing all their fields (plus perhaps overhead for a vtable pointer and alignment). Derived classes of Enemy could have additional fields and therefore be larger, so it doesn't let you store a derived object of Enemy in the space reserved for an actual Enemy object. When you do an assignment as in the example, it uses the assignment operator (in this case, defined implicitly)-- which sets the values in the left hand side object's fields to the values of the corresponding fields in the right hand side object, leaving the left-hand-side object's type (and so vtable pointer) unchanged. This is called "object slicing" and is generally to be avoided.
Pointers are all the same size, so you can put a pointer to a derived object of Enemy in the space for a pointer to Enemy and use it just as if it were a pointer to a plain enemy object. Since the pointer to a derived object points to an actual instance of the derived object, calls to virtual functions on the pointer will use the derived object's vtable and give you the desired behavior.
In c++, this is called slicing.
Enemy() creates a Enemy object. If you were to call Enemy().attack(), it would not print anything, because that method is empty.
The only way you can get polymorphic behavior in C++ is using pointers or references.
Using pointers is how polymorphism is implemented in c++ (see here). You'll get a type mismatch error if you try to put a monster or a ninja object into an array of enemies. But "a pointer to a derived class is type-compatible with a pointer to its base class."
That will give you an entirely different result.
In the first scenario with pointers, you'll have Enemy pointers that will point at your Ninja and Monster objects. The objects would be intact, and at runtime the attack() call would call the object's attack() method.
In the other scenario you have actual Enemy objects. When you'll assign the Ninja and Monster objects only the common members will be copied (the rest of the members that don't belong to Enemy will be lost).
Then when you'll call attack() it will be an Enemy attack() (Because they are Enemy objects)
Enemy enemies[2]; creates an array of objects of a concrete type (Enemy). That means, among other things, that all elements of that array have a known size.
How does that work with derived classes which might include other data? It doesn't.
On the other hand, given pointers, it doesn't matter at all. The pointer will point to "something" (the vtable plus data) and the virtual inheritance mechanism somehow figures out what's what and where's what. There may be the same functions, overloaded ones, additional data fields, it will still work.
Assigning monster and ninja to your Enemy array will work, however, when you call the function attack on each one, it will call the base class's attack function. Why? First, when you assign the objects into the Enemy array, you're essentially typecasting those classes so when you interact with its objects, they act like Enemy's as opposed to what they originally were.
If you noticed, you declared your attack function in Enemy as virtual. What this allows is essential in polymorphism. By declaring that function as virtual, you allow subclassed objects (Monster and Ninja, for example) of your Enemy to determine, at run-time, which version of the function attack to use if an Enemy pointer is used. This allows you to use a generic Enemy pointer to access different subclassed objects and still correctly use the right function:
Enemy * ptr;
Enemy copy;
Monster m;
copy = (Enemy)m;
ptr = &m;
copy.attack(); // Calls Enemy's definition of attack, which is undefined.
ptr->attack(); // Even though this is an Enemy pointer, the Monster's definition of attack is used.
By writing
enemies[0] = monster;
you are converting your Monster object to an Enemy object. Every derived class object can be converted automatically to a base class object. This is called object slicing. Once that conversion has happened the Enemy object no longer has any way of remembering that it once used to be a Monster object, it's just a plain Enemy object like any other. So when you call attack, you call Enemy::attack.
This issue doesn't arise in Java because in Java everything is a pointer automatically.
It isn't supported because when you assign a superclass instance the value of a subclass instance, the subclass information not in the superclass is culled. Ergo, some methods - even polymorphic ones - which are subclass-dependent wouldn't work in all situations. The only general-purpose way to guarantee type-safety at compile time is to use the parent class's implementation.
Short version: instances of the parent class may have less state than child class instances, so operations on instances of the parent class have to assume they are those defined for the parent class. Pointers obviate this since child class instances with full state do exist.
Because it is impossible (maybe very difficult and maybe can be done using pointers) to implement such functionality. The main reason is that base and derived objects can have different sizes (sizeof(Enemy) != sizeof(Monster)), and storing monsters in enemies you will just loose some data.
Related
I'm creating this question because I can't find any related solutions in dynamic_cast of object array on web, or just because I did not understand the concept of it, please lead me, Thank you
I'm currently working on a assignment which I have to use dynamic_cast to cast base class object (pointer) into it's subclasses object, to have access to others method in subclasses.
First of all I'll have this array
Hardware *object = new Hardware[100];
When it comes to need for access to subclasses of Hardware , I think I should do something like this:
Desktop *temp = dynamic_cast<Desktop*>(&object[0]);
But the temp resulting in NULL pointer
I asked some friend and they said I'll have to do something like
object[0] = new Desktop(//arguments);
Then only I can proceed to dynamic_cast of object[0], but it shows error(red lines) on the = operator :
no operator "=" matches these operands operand types are: Hardware = Desktop *
At the end I've tried many that similar to object[0] = new Desktop(//arguments); But didn't worked out, is there any steps that I've missed?
class Hardware //This is the base class
{
private:
string code;
double price;
public:
Hardware();
Hardware(string a, double b){
code = a;
price = b;
}
virtual void foo(){}
}
class Laptop: public Hardware //More of subclasses like this
{
private:
string brand;
public:
Laptop();
Laptop(string a, string b, double c):Hardware(b, c) {
brand = a;
}
}
int main(){
//Have to create one base class object with ptr array
Hardware *object = new Hardware[100];
//friend suggest: object[0] = new Desktop("Acer", "COMP-001", 1200.00);
//I wanted to use dynamic_cast like this
Desktop *temp = dynamic_cast<Desktop*>(&object[0]);
//To access to subclasses method and constructor
temp->displayLaptop();
}
Expecting results that I can downcast the Hardware class into Subclass Desktop, and be able to assign arguments into Desktop's constructor
The way you are doing it is incorrect. The types stored in the object array are of type Hardware. You can only successfully dynamic_cast those values to Hardware or anything that Hardware inherits (which is nothing).
Assuming Desktop is a subclass of Hardware (you did not actually show it), then dynamic_cast<Desktop*>(&object[0]) is guaranteed to return nullptr.
The way arrays work is they allocate memory to store a specific data type arranged sequentially in memory. You cannot expect to just treat that data type as a derived type that might (and in your case, does) contain more data.
If you wish to have an array of arbitrary hardware items, you will need to store each item as a pointer:
Hardware** object = new Hardware*[100];
std::fill(object, object + 100, NULL);
object[0] = new Desktop;
Your friend was correct, but you failed to change the type of object to store pointer types. That's what the compiler error is telling you.
At this stage, I highly recommend you use std::vector instead of allocating and managing that memory:
std::vector<Hardware*> object(100);
object[0] = new Desktop;
Even better still, use smart pointers:
// Example with shared pointers
std::vector<std::shared_ptr<Hardware>> object;
object.emplace_back(std::make_shared<Desktop>(/* args */));
// Example with unique pointers
std::vector<std::unique_ptr<Hardware>> object;
object.emplace_back(std::make_unique<Desktop>(/* args */));
You misunderstood what dynamic casting does.
Example of use:
Say you have two classes Square and Circle and both of them inherit from class Shape.
A producer creates either a Square or a Circle but passes you a pointer to a Shape.
Then via dynamic casting you can try and convert the pointer to Shape to a pointer Circle. If the Producer made a Square then the casting will fail return nullptr. If it was indeed a Circle then you will get a pointer to Circle.
In your case you create Shape and try to use dynamic casting to convert the pointer to a Square... it doesn't work this way.
I want to write a function which takes as an argument an object from the class "Armor", however, when I call the function, I use instead an object from the base class "Item". Of course this object I use, even though may only be considered an "Item", may also be an "Armor". Only when I am sure it is an "Armor", I want to call the function.
In my case, I store items in a vector (these items can be armors). I want to get this item from the vector and call the function with it (equip the item, which I know is an armor).
class Item{
};
class Armor : public Item{
};
void equipArmor(Armor armor){ //Armor class argument
//Equip the armor
}
int main(){
vector<Item> items;
Armor a;
items.push_back(a);
equipArmor(items[0]); //Call function with an "Item" as an argument (even though it is in fact also an "Armor")
}
The problem
You have a vector of Item. When you push_back an Armor it will be sliced into an Item. So in the vector you have no longer an Armor but just an ordinary Item.
This is why your code won't work. First you cannot call your equipArmor() function with an Item, since it expects an Armor and downcasting is never implicit. But even if you could, you would always pass an Item value and never an Armor value.
The solution
To solve your issue, you need to work with pointers (better smart pointers) or references.
The first thing you will need to be able to work with polymorphic types and doing runtime type determination, is to have at least one virtual function in your base class:
class Item{
public:
virtual ~Item() {}
};
Now let's make your vector a vector of shared pointers. The nice thing is that those will ensure that objects will be destroyed when they are no longer used in any shared pointer. So less hassle with memory management, and less hassle with the rule of 3 :
vector<shared_ptr<Item>> items;
shared_ptr<Item> a = make_shared<Armor>();
items.push_back(a);
shared_ptr<Item> i = make_shared<Item>();
items.push_back(i);
equipArmor(items[0]); // lets just try the two possible cases
equipArmor(items[1]);
Finally, in your function, you can then sense for the real type and act accordingly, in a safe manner using dynamic_pointer_cast:
void equipArmor(shared_ptr<Item> item){ //Armor class argument
shared_ptr<Armor> a = dynamic_pointer_cast<Armor>(item);
if (a) {
cout << "Armor"<<endl;
}
else cout << "Item"<<endl;
}
Online demo
Remarks
If your type is not polymorphic, you cannot you dynamic_pointer_cast. You still could cast with a static_pointer_cast, but this is risky, because it requires you to know for sure that the casted smart pointer has the right type.
If you prefer raw pointers, you the same principles would apply, but yo'ud use dynamic_cast or static_cast respectively. But again, static_cast requires you to be absolutely sure of the type. And how can you be, if you have a vector full of random items ?
You want to cast from the base class (Item) to the subclass (Armor). This is impossible.
You could do this if items would be a vector of pointers to Items. You can then cast an Item * to an Armor * if you are sure that the underlying object is actually an Armour.
int main(){
std::vector<Item *> items;
Armor a;
items.push_back(&a);
// This only works if items[0] points to an Armor object
equipArmor(*static_cast<Armor *>(items[0]));
}
I'm currently programming a game in C++. This game features a GameManager class. The GameManager class contains a map that holds pointers to game objects. I have defined a GameObject class that is an abstract class acting simply as an interface.
I have defined two classes that derive from the GameObject class: Enemy and Loot.
I want my GameManager class to contain a map of game objects, or rather, pointers to game objects. Because my GameManager owns these objects, I want the map to contain std::unique_ptr's.
However, I'm having a difficult time actually adding derived objects (e.g. Enemy and Loot) to this map.
I want my GameManager to iterate over the game objects and call the abstract methods. Essentially, my GameManager does not care whether or not something is an enemy, loot, or whatever, it just wants to be able to call the "draw" method as declared in the base class.
How would I go about adding a unique_ptr, that points to a derived class, to a map that contains unique_ptr's to the base class? My attempts so far lead to code I can't compile. I keep getting an error that states I am not allowed to dynamically cast a derived class pointer to a base class pointer.
I feel like this work fine if I was using raw pointers, but I'm intent on using smart pointers.
Code:
#include <memory>
#include <map>
class GameObject
{
public:
virtual void draw() = 0;
};
class Enemy : GameObject
{
public:
void draw() {};
};
class Loot : GameObject
{
public:
void draw() {};
};
int main()
{
std::map<int, std::unique_ptr<GameObject>> my_map;
// How do I add an Enemy or Loot object unique_ptr to this map?
my_map[0] = dynamic_cast<GameObject>(std::unique_ptr<Enemy>().get()); // doesn't compile, complains about being unable to cast to abstract class
return 0;
}
The first cause of an error message is that a class type can never be used as the type for a dynamic_cast. The target type of dynamic_cast must always be either a pointer to class type (meaning the result is null if the cast fails) or a reference to class type (meaning to throw an exception if the cast fails).
So improvement #1:
my_map[0] = dynamic_cast<GameObject*>(std::unique_ptr<Enemy>().get());
But this won't work because GameObject is a private base class of Enemy. You probably meant to use public inheritance, but (when using class instead of struct) you must say so:
class Enemy : public GameObject
// ...
Next we'll find that the = within the map statement is invalid. The left side has type std::unique_ptr<GameObject>, which does not have any operator= that can take a GameObject* pointer. But it does have a reset member for setting a raw pointer:
my_map[0].reset(dynamic_cast<GameObject*>(std::unique_ptr<Enemy>().get()));
Now the statement should compile - but it's still wrong.
Before getting to why it's wrong, we can make a simplification. dynamic_cast is needed for getting a pointer to derived class from a pointer to base class, or for many other type changes within a more complicated inheritance tree. But it's not needed at all to get a pointer to base class from a pointer to derived class: this is a valid implicit conversion, since every object with a derived class type must always contain a subobject of the base class type, and there's no "failure" case. So the dynamic_cast here can just be dropped.
my_map[0].reset(std::unique_ptr<Enemy>().get());
The next problem is that std::unique_ptr<Enemy>() creates a null unique_ptr, and no Enemy object is created at all. To create an actual Enemy, we can write instead either std::unique_ptr<Enemy>(new Enemy) or std::make_unique<Enemy>().
my_map[0].reset(std::make_unique<Enemy>().get());
Still wrong, and in a slightly tricky way. Now the problem is that the created Enemy object is owned by the temporary std::unique_ptr<Enemy> object returned by make_unique. The reset tells the std::unique_ptr<GameObject> within the map that it should own a pointer to the same object. But at the end of the statement, the temporary std::unique_ptr<Enemy> gets destroyed, and it destroys the Enemy object. So the map is left with a pointer to a dead object, which is invalid to use - not what you wanted.
But the solution here is that we don't need to mess around with get() and reset() at all. There is an operator= that allows assigning an rvalue std::unique_ptr<Enemy> to a std::unique_ptr<GameObject>, and it does the right thing here. It makes use of the implicit conversion from Enemy* to GameObject*.
my_map[0] = std::make_unique<Enemy>();
(Note if you had a named std::unique_ptr<Enemy>, you would need to std::move it to allow the assignment, as in my_map[0] = std::move(enemy_ptr);. But std::move is not needed above because the result of make_unique is already an rvalue.)
Now this statement is much shorter and more legible, and will actually do what you want.
A comment also suggested the possibility
my_map.emplace(0, std::make_unique<Enemy>());
This is also valid, but there's a possibly important difference: if the map already has an object with key zero, the = version will destroy and replace the old one, but the emplace version will leave the map alone and the just-created Enemy will be destroyed instead.
dynamic_cast can only be used to convert between pointers, and references. GameObject is neither a pointer type, nor a reference type, so you cannot dynamic_cast to it.
You may have intended dynamic_cast<GameObject*> instead. however, you shouldn't dynamic_cast to a (pointer to) a base class. A pointer to derived type is implicitly convertible to the base class pointer. Use static_cast when implicit conversion is not desirable. Furthermore, that conversion is not possible either, since the cast is outside of any member function, and therefore cannot have access to the private base class.
Furthermore, you cannot assign a bare pointer to a unique pointer. To transfer ownership of a bare pointer to a unique pointer, you can use unique_ptr::reset. Howver, you should never store a pointer from unique_ptr::get into another unique pointer. Doing so will result in undefined behaviour when both unique pointers destructors attempt to destroy the same object. Luckily in this case the pointer is value initialized, and therefore null, so the mistake has technically no consequences. But did you use null pointer intentionally? I suspect not.
Inserting a unique pointer to derived object into a map of unique pointers to base is simple. Let ptr be a unique pointer to Enemy:
std::unique_ptr<Enemy> ptr = get_unique_pointer_from_somewhere();
Simply move assign the unique pointer:
my_map[0] = std::move(ptr);
Or, you could use emplace member function of the map.
Finally, destructor unique_ptr<GameObject> will have undefined behaviour if it points to a derived object. To fix, declare the destructor of GameObject virtual.
I have some type hierarchy :
class GameObject{...};
class Subject:public GameObject{...};
class Player:public Subject{...};
class Bullet:public Subject{...};
class Enemy:public Subject{...};
In traversing the array of GameObject*, I check the condition and call method foo(*object1, *object2) ( object1 is reference to Bullet, object2 is reference to Player).
std::vector<GameObject*> objects;
// fill array ( pointers to Bullet, Enemy, Player)
foreach(auto obj1 : objects)
{
foreach(auto obj2 : objects)
{
if(obj1.getID() != obj2.getID()
{
foo(*obj1, *obj2);
}
}
}
Also I write some overloaded foo methods :
void foo(GameObject&, GameObject&)
void foo(Bullet&, Player&)
void foo(Bullet&, Enemy&)
But only foo(GameObject&, GameObject&) is called. Why?
These are pointers to GameObject. You dereference them and get objects of type GameObject, and this makes sense. If you dereferenced a pointer to int and got an object of type Apple, that'd be very odd.
GameObject * (a pointer to GameObject) means that there exists some piece of memory that this pointer points to, and this memory should be treated as an object of type GameObject. When you dereference a pointer, you actually get access to that memory, which is obviously treated as GameObject.
foo() overload to be called is chosen during program compilation. And compiler obviously has no idea what are the real types of objects that you pass in your loops so it chooses the only thing it knows for sure.
There are various ways to workaround this problem in general, google "double dispatch" or "visitor pattern".
GameObject *pgo = new SpaceShip;
Asteroid *pa = new Asteroid;
pgo = pa;
GameObject& rgo = *pa;
Assuming GameObject is the base class and SpaceShip and Asteroid have public inheritance with GameOject. I understand the first 2 lines. I don't understand the 3rd line. Why is it you can set the addresses equal, arn't they different classes even if inherited? Then I guess I don't fully understand line 1. Also, I am not sure what line 4 is doing either.
If GameObject is an interface that both Asteroid & SpaceShip implement, a variable of type GameObject* can point to both of this types.
The 4th line uses a "By Reference" variable declaration to "convert" a pointer type variable to non-pointer.
You should read about C++'s inheritance and polymorphism. Basically, the third line works because an Asteroid is a GameObject, so you can access the GameObject-related members of the Asteroid using a GameObject * pointer.
There may be situations where you need to work with GameObjects, and some other situations where you need to work with Asteroids. In either case you'd use the appropriate pointer, as long the pointer type is within the inheritance hierarchy.
The last line is simply a reference to the object the pointer pa points to.