【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  阅读(146)  评论(0编辑  收藏  举报

导航