您的位置:首页 > 编程语言 > C语言/C++

【more effective c++读书笔记】【第5章】技术(7)——让函数根据一个以上的对象类型来决定如何虚化(1)

2015-09-09 18:29 417 查看
一个虚函数调用动作称为一个消息分派,如果某个函数调用根据两个参数而虚化就称为双重分派,根据多个函数而虚化称为多重分派。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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: