instanceof equivalent in C++ [duplicate]

三世轮回 提交于 2019-12-23 01:41:30

问题


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 Trucks, 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 addObjects 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!