zoukankan      html  css  js  c++  java
  • 【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;
    }


    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    STM32 时钟配置分析
    STM32 开发板资源梳理
    STM32 摄像头实验OV2640
    STM32 TFT液晶屏与FSMC
    STM32 开发板电源与供电方式
    视觉里程计07 Qt的一些bug修改记录
    解决wireshark检测不到网卡的问题
    gdb 脚本调试
    [转] GCC 中的编译器堆栈保护技术
    使用gdbserver远程调试
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4921351.html
Copyright © 2011-2022 走看看