问题
I'm creating a 2D game in C++ that uses levels made out of tiles. The world class has an add(WorldObject* o)
function that can both accept a tile or an entity such as an enemy. Both the Tile
and the Entity
class are derived from WorldObject
. In every case, the object should be added to the entities
list; but if it is a tile, it should also be added to the tiles
list.
World.h
class World {
private:
list<WorldObject*> content;
list<Tile*> tiles;
public:
void add(WorldObject*);
}
World.cpp
void World::add(WorldObject* o) {
content.push_back(o);
if(*o instanceof Tile) //What do I need to put here?
tiles.push_back(o);
}
How do I check whether an object has a specific type in C++? It's nothing about typecasting and virtual functions and things like that because I don't want to invoke functions to the object at this time; I just need to add it to a separate list if it has a certain type.
In Java, I can do if(instance instanceof Class)
. How can I do this in C++?
回答1:
A dynamic_cast
will check to see if you can downcast o
to Tile
. If you can, it will return a valid Tile*
, else it will return a null Tile*
:
void World::add(WorldObject* o) {
content.push_back(o);
if (Tile* t = dynamic_cast<Tile*>(o)) {
tiles.push_back(t);
}
}
回答2:
You can only infer the dynamic type of an object at run-time if the object is polymorphic, ie has at least one virtual
function member. A virtual
destructor will also do for this matter. (Actually, talking about a “dynamic type” doesn't make much sense if nothing is virtual
anyway.)
Then you can try a dynamic_cast
as an instanceof
equivalence. Where you would have written
void f(Base base) {
if (base instanceof Derived) {
Derived derived = (Derived) base;
// use derived
}
}
in Java, you would write
void
f(Base& base)
{
if (Derived * derived_ptr = dynamic_cast<Derived *>(&base))
{
// use derived_ptr
}
}
in C++. A dynamic_cast
to a pointer type will simply return a nullptr
(which evaluates to false
) if the object is not of the right type so it is usually seen inside an if
. A dynamic_cast
to a reference, however, would throw an exception so you should only use it if you are sure that the object actually is of that type.
I should add that the C++ example will only be valid if Derived
is actually derived from Base
. Otherwise the check would have been rather pointless anyway. Note however that you cannot restore the type of an object through a void *
pointer. It makes sense if you think about it because that void *
might actually point to a random collection of bytes that has nothing whatsoever to extract a type from.
回答3:
You can do this:
if(dynamic_cast<Tile*>(o))
This works because dynamic_cast
on a pointer returns null if the type is not compatible. And of course, a null pointer is "falsy", so the check will fail. If o
is in fact a Tile
, a non-null Tile*
will result, and the check will succeed.
回答4:
This should be an answer, but it may not be /the/ answer. What you're doing is awful. You will have something like:
void addObject(Object* ob) {
if(ob->isA()) { doA(ob); }
if(ob->isB()) { doB(ob); }
if(ob->isC()) { doC(ob); }
genericObjectStuff(ob);
}
This is an "antipattern" because it is bad, it means for each derived you have (in your case Tile
is CURRENTLY the only one) you'll have to come back to this function and add any special behaviour.
There are two fixes (that depend on what the objects in play are like)
1) Virtual functions:
void addObject(Object* ob) {
ob->doSpecificThingYouNeedToDo(this);
genericObjectStuff(ob);
}
Where doSpecificThingYouNeedToDo
will doA(this);
inside if it is an A, do B if it is a B.... thus you write what it does when it is added where you define the class! Much better!
2) You've abstracted away too much!
If I give you a DrivableThing
you know you can do whatever it means to be a DrivableThing
to it, you are not given that it is a Truck
say (which IS A DrivableThing
) so you cannot loadCargo
on the drivable thing because it might not be a truck!
So a function that loads cargo can only accept Truck
s, or some other type that is drivable and loadable. It'd be wrong to have a generic function that loaded trucks, but also put pizzas on the back of motorbikes (Delivery task also!)
This is the opposite of 1, rather than adding the functionality to the derived classes, you want different addObject
s that accept the specific object only. Because they require this information, so you currently have:
void loadUpCargo(DrivableThing* thing) {
if(thing->isPizzaBike()) { thing->addPizzas(whatever); }
if(thing->isTruck()) { thing->addCrates(whatever); }
thing->driveOff(); //we can always drive off DrivableThings
}
You can change this to:
void loadUpCargo(Truck* truck) {
truck->addCrates(whatever);
truck->driveOff();
}
void loadUpCargo(PizzaBike* bike) {
bike->addPizza(whatever);
bike->driveOff();
}
Much better!
The classic example of this is "Animal class has an Eat method" "Bananas are Eatable, a Monkey is an Animal" but having an Animal and an Eatable wont stop you feeding Grass to a Monkey because you've abstracted the information away!
来源:https://stackoverflow.com/questions/27595076/instanceof-equivalent-in-c