【more effective c++读书笔记】【第5章】技术(7)——让函数根据一个以上的对象类型来决定如何虚化(1)
一个虚函数调用动作称为一个消息分派,如果某个函数调用根据两个参数而虚化就称为双重分派,根据多个函数而虚化称为多重分派。C++不支持双重分派和多重分派,因此我们必须自己实现。有以下几种方法:
一、虚函数 + RTTI(运行时期类型辨识)
//GameObject.h #ifndef GAMEOBJECT_H #define GAMEOBJECT_H class GameObject{ //抽象基类 public: virtual void collide(GameObject& otherObject) = 0; }; class SpaceShip : public GameObject{ //宇宙飞船类 public: virtual void collide(GameObject& otherObject); }; class SpaceStation : public GameObject{ //太空站类 public: virtual void collide(GameObject& otherObject); }; class Asteroid : public GameObject{ //小行星类 public: virtual void collide(GameObject& otherObject); }; class CollisionWithUnkonwnObject{//异常类 public: CollisionWithUnkonwnObject(GameObject& whatWeHit); }; #endif //GameObject.cpp #include"GameObject.h" #include<iostream> void SpaceShip::collide(GameObject& otherObject){ const type_info& objectType = typeid(otherObject); if (objectType == typeid(SpaceShip)){ SpaceShip& ss = static_cast<SpaceShip&>(otherObject); std::cout << "SpaceShip=>SpaceShip" << std::endl; } else if (objectType == typeid(SpaceStation)){ SpaceStation& ss = static_cast<SpaceStation&>(otherObject); std::cout << "SpaceShip=>SpaceStation" << std::endl; } else if (objectType == typeid(Asteroid)){ Asteroid& a = static_cast<Asteroid&>(otherObject); std::cout << "SpaceShip=>Asteroid" << std::endl; } else { throw CollisionWithUnkonwnObject(otherObject); } } void SpaceStation::collide(GameObject& otherObject){ const type_info& objectType = typeid(otherObject); if (objectType == typeid(SpaceShip)){ SpaceShip& ss = static_cast<SpaceShip&>(otherObject); std::cout << "SpaceStation=>SpaceShip" << std::endl; } else if (objectType == typeid(SpaceStation)){ SpaceStation& ss = static_cast<SpaceStation&>(otherObject); std::cout << "SpaceStation=>SpaceStation" << std::endl; } else if (objectType == typeid(Asteroid)){ Asteroid& a = static_cast<Asteroid&>(otherObject); std::cout << "SpaceStation=>Asteroid" << std::endl; } else { throw CollisionWithUnkonwnObject(otherObject); } } void Asteroid::collide(GameObject& otherObject){ const type_info& objectType = typeid(otherObject); if (objectType == typeid(SpaceShip)){ SpaceShip& ss = static_cast<SpaceShip&>(otherObject); std::cout << "Asteroid=>SpaceShip" << std::endl; } else if (objectType == typeid(SpaceStation)){ SpaceStation& ss = static_cast<SpaceStation&>(otherObject); std::cout << "Asteroid=>SpaceStation" << std::endl; } else if (objectType == typeid(Asteroid)){ Asteroid& a = static_cast<Asteroid&>(otherObject); std::cout << "Asteroid=>Asteroid" << std::endl; } else { throw CollisionWithUnkonwnObject(otherObject); } } CollisionWithUnkonwnObject::CollisionWithUnkonwnObject(GameObject& whatWeHit){ std::cout << "异常类" << std::endl; } //main.cpp #include"GameObject.h" #include<iostream> using namespace std; int main(){ SpaceShip sp; SpaceStation ss; Asteroid ad; sp.collide(sp); sp.collide(ss); sp.collide(ad); cout << "-----------" << endl; ss.collide(sp); ss.collide(ss); ss.collide(ad); cout << "-----------" << endl; ad.collide(sp); ad.collide(ss); ad.collide(ad); system("pause"); return 0; }
这种方法会破坏封装,因为每一个collide函数都必须知道其每一个兄弟类。如果有新型对象加入上述例子中,必须修改上述例子中的每一个可能遭遇新对象的RTTI-based if-then-else链。
二、只使用虚函数
//GameObject.h #ifndef GAMEOBJECT_H #define GAMEOBJECT_H class SpaceShip; class SpaceStation; class Asteroid; class GameObject{ //抽象基类 public: virtual void collide(GameObject& otherObject) = 0; virtual void collide(SpaceShip& otherObject) = 0; virtual void collide(SpaceStation& otherObject) = 0; virtual void collide(Asteroid& otherObject) = 0; }; class SpaceShip : public GameObject{ //宇宙飞船类 public: virtual void collide(GameObject& otherObject); virtual void collide(SpaceShip& otherObject); virtual void collide(SpaceStation& otherObject); virtual void collide(Asteroid& otherObject); }; class SpaceStation : public GameObject{ //太空站类 public: virtual void collide(GameObject& otherObject); virtual void collide(SpaceShip& otherObject); virtual void collide(SpaceStation& otherObject); virtual void collide(Asteroid& otherObject); }; class Asteroid : public GameObject{ //小行星类 public: virtual void collide(GameObject& otherObject); virtual void collide(SpaceShip& otherObject); virtual void collide(SpaceStation& otherObject); virtual void collide(Asteroid& otherObject); }; class CollisionWithUnkonwnObject{//异常类 public: CollisionWithUnkonwnObject(GameObject& whatWeHit); }; #endif //GameObject.cpp #include"GameObject.h" #include<iostream> void SpaceShip::collide(GameObject& otherObject){ otherObject.collide(*this); } void SpaceShip::collide(SpaceShip& otherObject){ std::cout << "SpaceShip=>SpaceShip" << std::endl; } void SpaceShip::collide(SpaceStation& otherObject){ std::cout << "SpaceShip=>SpaceStation" << std::endl; } void SpaceShip::collide(Asteroid& otherObject){ std::cout << "SpaceShip=>Asteroid" << std::endl; } void SpaceStation::collide(GameObject& otherObject){ otherObject.collide(*this); } void SpaceStation::collide(SpaceShip& otherObject){ std::cout << "SpaceStation=>SpaceShip" << std::endl; } void SpaceStation::collide(SpaceStation& otherObject){ std::cout << "SpaceStation=>SpaceStation" << std::endl; } void SpaceStation::collide(Asteroid& otherObject){ std::cout << "SpaceStation=>Asteroid" << std::endl; } void Asteroid::collide(GameObject& otherObject){ otherObject.collide(*this); } void Asteroid::collide(SpaceShip& otherObject){ std::cout << "Asteroid=>SpaceShip" << std::endl; } void Asteroid::collide(SpaceStation& otherObject){ std::cout << "Asteroid=>SpaceStation" << std::endl; } void Asteroid::collide(Asteroid& otherObject){ std::cout << "Asteroid=>Asteroid" << std::endl; } CollisionWithUnkonwnObject::CollisionWithUnkonwnObject(GameObject& whatWeHit){ std::cout << "异常类" << std::endl; } //main.cpp #include"GameObject.h" #include<iostream> using namespace std; int main(){ SpaceShip sp; SpaceStation ss; Asteroid ad; sp.collide(sp); sp.collide(ss); sp.collide(ad); cout << "-----------" << endl; ss.collide(sp); ss.collide(ss); ss.collide(ad); cout << "-----------" << endl; ad.collide(sp); ad.collide(ss); ad.collide(ad); system("pause"); return 0; }
上述例子的基本思想是将双重分派以两个单一分派(也就是两个分离的虚函数调用)实现出来,其一用来决定第一对象的动态类型,其二用来决定第二对象的动态类型。缺点是与虚函数+ RTTI解法一样,每个类都必须知道其兄弟类。一旦有新的类加入,代码就必须修改。
三、自行仿真虚函数表格(使用成员函数的碰撞处理函数)
//GameObject.h #ifndef GAMEOBJECT_H #define GAMEOBJECT_H #include<string> #include<map> 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 std::map<std::string, HitFunctionPtr> HitMap; //在函数表中查找需要的碰撞函数 static HitFunctionPtr lookup(const GameObject& whatWeHit); //建立函数表 static HitMap* initializeCollisionMap(); }; class SpaceStation : 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(SpaceStation::*HitFunctionPtr)(GameObject&);//成员函数指针 //函数表的类型:每项关联了碰撞函数参数的动态类型名和碰撞函数本身 typedef std::map<std::string, HitFunctionPtr> HitMap; //在函数表中查找需要的碰撞函数 static HitFunctionPtr lookup(const GameObject& whatWeHit); //建立函数表 static HitMap* initializeCollisionMap(); }; class Asteroid : 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(Asteroid::*HitFunctionPtr)(GameObject&);//成员函数指针 //函数表的类型:每项关联了碰撞函数参数的动态类型名和碰撞函数本身 typedef std::map<std::string, HitFunctionPtr> HitMap; //在函数表中查找需要的碰撞函数 static HitFunctionPtr lookup(const GameObject& whatWeHit); //建立函数表 static HitMap* initializeCollisionMap(); }; class CollisionWithUnkonwnObject{//异常类 public: CollisionWithUnkonwnObject(GameObject& whatWeHit); }; #endif //GameObject.cpp #include"GameObject.h" #include<memory> #include<iostream> void SpaceShip::collide(GameObject& otherObject){ HitFunctionPtr hfp = lookup(otherObject); if (hfp) (this->*hfp)(otherObject); else throw CollisionWithUnkonwnObject(otherObject); } void SpaceShip::hitSpaceShip(GameObject& spaceShip){ SpaceShip& ss = dynamic_cast<SpaceShip&>(spaceShip); std::cout << "SpaceShip=>SpaceShip" << std::endl; } void SpaceShip::hitSpaceStation(GameObject& spaceStation){ SpaceStation& ss = dynamic_cast<SpaceStation&>(spaceStation); std::cout << "SpaceShip=>SpaceStation" << std::endl; } void SpaceShip::hitAsteroid(GameObject& asteroid){ Asteroid& as = dynamic_cast <Asteroid&>(asteroid); std::cout << "SpaceShip=>Asteroid" << std::endl; } //在函数表中查找需要的碰撞函数 SpaceShip::HitFunctionPtr SpaceShip::lookup(const GameObject& whatWeHit){ static std::auto_ptr<HitMap> collisionMap(initializeCollisionMap()); HitMap::iterator mapEntry = (*collisionMap).find(typeid(whatWeHit).name()); if (mapEntry == (*collisionMap).end()) return 0; return (*mapEntry).second; } //建立函数表 SpaceShip::HitMap* SpaceShip::initializeCollisionMap(){ HitMap* phm = new HitMap; (*phm)["class SpaceShip"] = &hitSpaceShip;//对应typeid(whatWeHit).name() (*phm)["class SpaceStation"] = &hitSpaceStation; (*phm)["class Asteroid"] = &hitAsteroid; return phm; } void SpaceStation::collide(GameObject& otherObject){ HitFunctionPtr hfp = lookup(otherObject); if (hfp) (this->*hfp)(otherObject); else throw CollisionWithUnkonwnObject(otherObject); } void SpaceStation::hitSpaceShip(GameObject& spaceShip){ SpaceShip& ss = dynamic_cast<SpaceShip&>(spaceShip); std::cout << "SpaceStation=>SpaceShip" << std::endl; } void SpaceStation::hitSpaceStation(GameObject& spaceStation){ SpaceStation& ss = dynamic_cast<SpaceStation&>(spaceStation); std::cout << "SpaceStation=>SpaceStation" << std::endl; } void SpaceStation::hitAsteroid(GameObject& asteroid){ Asteroid& as = dynamic_cast <Asteroid&>(asteroid); std::cout << "SpaceStation=>Asteroid" << std::endl; } //在函数表中查找需要的碰撞函数 SpaceStation::HitFunctionPtr SpaceStation::lookup(const GameObject& whatWeHit){ static std::auto_ptr<HitMap> collisionMap(initializeCollisionMap()); HitMap::iterator mapEntry = (*collisionMap).find(typeid(whatWeHit).name()); if (mapEntry == (*collisionMap).end()) return 0; return (*mapEntry).second; } //建立函数表 SpaceStation::HitMap* SpaceStation::initializeCollisionMap(){ HitMap* phm = new HitMap; (*phm)["class SpaceShip"] = &hitSpaceShip;//对应typeid(whatWeHit).name() (*phm)["class SpaceStation"] = &hitSpaceStation; (*phm)["class Asteroid"] = &hitAsteroid; return phm; } void Asteroid::collide(GameObject& otherObject){ HitFunctionPtr hfp = lookup(otherObject); if (hfp) (this->*hfp)(otherObject); else throw CollisionWithUnkonwnObject(otherObject); } void Asteroid::hitSpaceShip(GameObject& spaceShip){ SpaceShip& ss = dynamic_cast<SpaceShip&>(spaceShip); std::cout << "Asteroid=>SpaceShip" << std::endl; } void Asteroid::hitSpaceStation(GameObject& spaceStation){ SpaceStation& ss = dynamic_cast<SpaceStation&>(spaceStation); std::cout << "Asteroid=>SpaceStation" << std::endl; } void Asteroid::hitAsteroid(GameObject& asteroid){ Asteroid& as = dynamic_cast <Asteroid&>(asteroid); std::cout << "Asteroid=>Asteroid" << std::endl; } //在函数表中查找需要的碰撞函数 Asteroid::HitFunctionPtr Asteroid::lookup(const GameObject& whatWeHit){ static std::auto_ptr<HitMap> collisionMap(initializeCollisionMap()); HitMap::iterator mapEntry = (*collisionMap).find(typeid(whatWeHit).name()); if (mapEntry == (*collisionMap).end()) return 0; return (*mapEntry).second; } //建立函数表 Asteroid::HitMap* Asteroid::initializeCollisionMap(){ HitMap* phm = new HitMap; (*phm)["class SpaceShip"] = &hitSpaceShip;//对应typeid(whatWeHit).name() (*phm)["class SpaceStation"] = &hitSpaceStation; (*phm)["class Asteroid"] = &hitAsteroid; return phm; } CollisionWithUnkonwnObject::CollisionWithUnkonwnObject(GameObject& whatWeHit){ std::cout << "异常类" << std::endl; } //main.cpp #include"GameObject.h" #include<iostream> using namespace std; int main(){ SpaceShip sp; SpaceStation ss; Asteroid ad; sp.collide(sp); sp.collide(ss); sp.collide(ad); cout << "-----------" << endl; ss.collide(sp); ss.collide(ss); ss.collide(ad); cout << "-----------" << endl; ad.collide(sp); ad.collide(ss); ad.collide(ad); system("pause"); return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
posted on 2015-09-09 18:29 ruan875417 阅读(145) 评论(0) 编辑 收藏 举报