我正在用C ++创建一个使用瓷砖制作的2D游戏。世界级有一个add(WorldObject* o)
函数,可以接受一个瓦片或一个实体,比如一个敌人。 Tile
和Entity
类都来自WorldObject
。在每种情况下,都应将对象添加到entities
列表中;但如果它是一个图块,它也应该添加到tiles
列表中。
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);
}
如何检查对象在C ++中是否具有特定类型?这与类型转换和虚函数以及类似的东西无关,因为我不想在此时调用对象的函数;如果它有某种类型,我只需要将它添加到一个单独的列表中。
在Java中,我可以if(instance instanceof Class)
。我怎么能用C ++做到这一点?
答案 0 :(得分:10)
dynamic_cast
会检查您是否可以将o
转发给Tile
。如果可以,它将返回有效的Tile*
,否则它将返回空Tile*
:
void World::add(WorldObject* o) {
content.push_back(o);
if (Tile* t = dynamic_cast<Tile*>(o)) {
tiles.push_back(t);
}
}
答案 1 :(得分:4)
如果对象是多态的,即只有至少一个virtual
函数成员,则只能在运行时推断对象的动态类型。一个virtual
析构函数也会为此做这件事。 (实际上,如果没有virtual
,那么谈论“动态类型”并没有多大意义。)
然后,您可以尝试将dynamic_cast
作为instanceof
等值。你会写的地方
void f(Base base) {
if (base instanceof Derived) {
Derived derived = (Derived) base;
// use derived
}
}
在Java中,你会写
void
f(Base& base)
{
if (Derived * derived_ptr = dynamic_cast<Derived *>(&base))
{
// use derived_ptr
}
}
在C ++中。指针类型的dynamic_cast
只会返回nullptr
(评估为false
),如果对象不是正确的类型,那么它通常会在if
内看到。但是,引用的dynamic_cast
会引发异常,因此如果您确定该对象实际上属于该类型,则应该只使用它。
我应该补充一点,如果Derived
实际上是从Base
派生的,那么C ++示例才有效。否则,检查本来是毫无意义的。但请注意,您无法通过void *
指针恢复对象的类型。如果你考虑它是有道理的,因为void *
实际上可能指向一个随机的字节集合,它没有任何东西从中提取类型。
答案 2 :(得分:2)
你可以这样做:
if(dynamic_cast<Tile*>(o))
这是有效的,因为如果类型不兼容,指针上的dynamic_cast
将返回null。当然,空指针是“假的”,因此检查将失败。如果o
实际上是Tile
,则会产生非空Tile*
,并且检查会成功。
答案 3 :(得分:0)
这应该是一个答案,但它可能不是/答案。你做的很糟糕。你会有类似的东西:
void addObject(Object* ob) {
if(ob->isA()) { doA(ob); }
if(ob->isB()) { doB(ob); }
if(ob->isC()) { doC(ob); }
genericObjectStuff(ob);
}
这是一个“反模式”,因为它很糟糕,它意味着每个派生的(在你的情况下Tile
当前是唯一的)你将不得不回到这个功能并添加任何特殊的行为。
有两个修复(取决于游戏中的对象是什么样的)
1)虚拟功能:
void addObject(Object* ob) {
ob->doSpecificThingYouNeedToDo(this);
genericObjectStuff(ob);
}
doSpecificThingYouNeedToDo
如果它是A doA(this);
,如果它是B,那么就做B ......因此你写下它在你定义类的地方时的作用!好多了!
2)你已经抽出太多了!
如果我给你一个DrivableThing
你知道你可以做任何意味着成为DrivableThing
,你就不会被认为是Truck
说的(这是一个DrivableThing
)所以你不能loadCargo
关于可驾驶的东西,因为它可能不是卡车!
因此,装载货物的功能只能接受Truck
或其他可驱动和可装载的类型。有一个装载卡车的通用功能是错误的,但也会在摩托车后面放上比萨饼(交付任务也是!)
这与1相反,而不是向派生类添加功能,您需要仅接受特定对象的不同addObject
。因为他们需要这些信息,所以您目前拥有:
void loadUpCargo(DrivableThing* thing) {
if(thing->isPizzaBike()) { thing->addPizzas(whatever); }
if(thing->isTruck()) { thing->addCrates(whatever); }
thing->driveOff(); //we can always drive off DrivableThings
}
您可以将其更改为:
void loadUpCargo(Truck* truck) {
truck->addCrates(whatever);
truck->driveOff();
}
void loadUpCargo(PizzaBike* bike) {
bike->addPizza(whatever);
bike->driveOff();
}
好多了!
这个经典的例子是“动物类有吃法”,“香蕉是可食用的,猴子是动物”,但有动物和食物不会阻止你喂草给猴子,因为你已经提取了信息远!