二重调度(三):只使用虚函数

其实有一个方法可以将用RTTI实现二重调度固有风险降到最低的,不过在此之前让我

们看一下怎么只用虚函数来解决二重调度问题。 这个方法和RTTI方法有这同样的基本构架。collide函数被申明为虚,并被所有派生类重定义,此外,它还被每个类重载,每个重载处理一个派生类型: 


class SpaceShip;                        // forward declarations 
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); 
  ... 

}; 


其基本原理就是用两个单一调度实现二重调度,也就是说有两个单独的虚函数调用:

第一次决定第一个对象的动态类型,第二次决定第二个对象动态类型。和前面一样,第一次虚函数调用带的是GameObject类型的参数。其实现是令人吃惊地简单: 


void SpaceShip::collide(GameObject& otherObject) 

{   

    otherObject.collide(*this); 


粗一看,它象依据参数的顺序进行循环调用,也就是开始的otherObject变成了调用
成员函数的对象,而*this成了它的参数。但再仔细看一下啦,它不是循环调用。你知道的,编译器根据参数的静态类型决定调那一组函数中的哪一个。在这儿,有四个不同的collide函数可以被调用,但根据*this的静态类型来选中其中一个。现在的静态类型是什么?因为是在SpaceShip的成员函数中,所以*this肯定是SpaceShip 类型。调用的将是接受SpaceShip参数的collide函数,而不是带GameOjbect类型参数的collide函数。 
所有的collide函数都是虚函数, 所以在SpaceShip::collide中调用的是otherObject

真实类型中实现的collide版本。在这个版本中,两个对象的真实类型都是知道的,左边的是*this (实现这个函数的类的类型) , 右边对象的真实类型是SpaceShip (申明的形参类型) 。 

看了SpaceShip类中的其它collide的实现,就更清楚了:

void SpaceShip::collide(SpaceShip& otherObject) 

  process a SpaceShip-SpaceShip collision; 


void SpaceShip::collide(SpaceStation& otherObject) 

  process a SpaceShip-SpaceStation collision; 


void SpaceShip::collide(Asteroid& otherObject) 

  process a SpaceShip-Asteroid collision; 


你看到了,一点都不混乱,也不麻烦,没有RTTI,也不需要为意料之外的对象类型抛

异常。不会有意料之外的类型的,这就是使用虚函数的好处。实际上,如果没有那个致命缺陷的话,它就是实现二重调度问题的完美解决方案。 


这个缺陷是,和前面看到的RTTI方法一样:每个类都必须知道它的同胞类。当增加新类时, 所有的代码都必须更新。 不过, 更新方法和前面不一样。 确实, 没有if...then...else需要修改,但通常是更差:每个类都需要增加一个新的虚函数。就本例而言,如果你决定增加一个新类Satellite(继承于GameObjcet) ,你必须为每个现存类增加一个collide函数。修改现存类经常是你做不到的。比如,你不是在写整个游戏,只是在完成程序框架下的一个支撑库,你可能无权修改GameObject类或从其经常的框架类。此时,增加一个新的成员函数(虚的或不虚的) ,都是不可能的。也就说,你理论上有操作需要被修改的类的权限,但实际上没有。打个比方,你受雇于Nitendo,使用一个包含GameObject和其它需要的类的运行库进行编程。当然不是只有你一个人在使用这个库,全公司都将震动于每次你决定在你的代码中增加一个新类型时,所有的程序都需要重新编译。实际中,广被使用的库极少被修改,因为重新编译所有用了这个库的程序的代价太大了。 


总结一下就是:如果你需要实现二重调度,最好的办法是修改设计以取消这个需要。
如果做不到的话,虚函数的方法比RTTI的方法安全,但它限制了你的程序的可控制性(取决于你是否有权修改头文件) 。另一方面,RTTI的方法不需要重编译,但通常会导致代码无法维护。自己做抉择啦!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值