有一个方法来增加选择。编译器通常创建一个函数指针数组(vtbl)来实现虚函数,并在虚函数被调用时在这个数组中进行下标索引。使用vtbl,编译器避免了使用if...then...else链,并能在所有调用虚函数的地方生成同样的代码:确定正确的vtbl下标,然后调用vtbl这个位置上存储的指针所指向的函数。 没理由说你不能这么做。如果这么做了,不但使得你基于RTTI的代码更具效率(下标索引加函数指针的反引用通常比if...then...else高效, 产生的代码也少) , 同样也将RTTI的使用范围限定在一处:你初始化函数指针数组的地方。提醒一下,看下面的内容前最好做一下深呼吸( I should mention that the meek may inherit the earth, but the meek of heart may wish to take a few deep breaths before reading what follows) 。
对GameObjcet继承体系中的函数作一些修改:
class GameObject {
public:
virtual void collide(GameObject& otherObject) = 0;
...
};
class SpaceShip: public GameObject {
public:
virtual void collide(GameObject& otherObject);
virtual void hitSpaceShip(GameObject& spaceShip);
virtual void hitSpaceStation(GameObject& spaceStation);
virtual void hitAsteroid(GameObject& asteroid);
...
private:
typedef void (SpaceShip::*HitFunctionPtr)(GameObject&);
typedef map<string, HitFunctionPtr> HitMap;
static HitMap * initializeCollisionMap();
static HitFunctionPtr lookup(const GameObject& whatWeHit);
};
{
SpaceShip& otherShip= dynamic_cast<SpaceShip&>(spaceShip);
process a SpaceShip-SpaceShip collision;
}
{
SpaceStation& station= dynamic_cast<SpaceStation&>(spaceStation);
process a SpaceShip-SpaceStation collision;
}
{
Asteroid& theAsteroid = dynamic_cast<Asteroid&>(asteroid);
process a SpaceShip-Asteroid collision;
}
SpaceShip::HitMap * SpaceShip::initializeCollisionMap()
{
HitMap *phm = new HitMap;
(*phm)["SpaceShip"] = &hitSpaceShip;
(*phm)["SpaceStation"] = &hitSpaceStation;
(*phm)["Asteroid"] = &hitAsteroid;
return phm;
}
SpaceShip::HitFunctionPtr
SpaceShip::lookup(const GameObject& whatWeHit)
{
static auto_ptr<HitMap> collisionMap(initializeCollisionMap());
// look up the collision-processing function for the type
// of whatWeHit. The value returned is a pointer-like
// object called an "iterator"
HitMap::iterator mapEntry= collisionMap.find(typeid(whatWeHit).name());
// mapEntry == collisionMap.end() if the lookup failed;
// this is standard map behavior. Again, see Item 35.
if (mapEntry == collisionMap.end())
return 0;
// If we get here, the search succeeded. mapEntry
// points to a complete map entry, which is a
// (string, HitFunctionPtr) pair. We want only the
// second part of the pair, so that's what we return.
return (*mapEntry).second;
//最后一句是return (*mapEntry).second而不是习惯上的mapEntry->second以满足
//STL的奇怪行为
}
void SpaceShip::collide(GameObject& otherObject)
{
HitFunctionPtr hfp = lookup(otherObject); // find the function to call
if (hfp) { // if a function was found
(this->*hfp)(otherObject); // call it
}
else {
throw CollisionWithUnknownObject(otherObject);
}
}