《More Effective C++》读书笔记-技术(二)

30、proxy classes(代理类)

可以用两个类来实现二维数组:Array1D是一个一维数组,而Array2D则是一个Array1D的一维数组。Array1D的实例扮演的是一个在概念上不存在的一维数组,它是一个代理类。

代理类最神奇的功能是区分通过operator[]进行的是读操作还是写操作,它的思想是对于operator[]操作,返回的不是真正的对象,而是一个 proxy类,这个代理类记录了对象的信息。将它作为赋值操作的目标时,proxy类扮演的是左值;用其它方式使用它时,proxy类扮演的是右值。用赋值操作符来实现左值操作,用隐式类型转换来实现右值操作。

用proxy类区分operator[]作左值还是右值的局限性:要实现proxy类和原类型的无缝替代,必须声明原类型的一整套操作符;另外,使用proxy类还有隐式类型转换的所有缺点。

31、让函数根据一个以上的对象类型来决定如何虚化

假设正在编写一个小游戏,游戏的背景是发生在太空,有宇宙飞船、太空船和小行星,它们可能会互相碰撞,而且其碰撞的规则不同,如何用C++代码处理物体间的碰撞。代码的框架如下:

class GameObject{...};
class SpaceShip:public GameObject{...};
class SpaceStation:public GameObject{...};
class Asteroid:public GameObject{...};

void checkForCollision(GameObject& obj1,GameObject& obj2)
{
    if(theyJustCollided(obj1,obj2))
    {
        processCollision(obj1,obj2);
    }
    else
    {
        ...
    }
}

当调用processCollision()时,obj1和obj2的碰撞结果取决于obj1和obj2的真实类型,但我们只知道它们是GameObject对象。相当于我们需要一种作用在多个对象上的虚函数。这类型问题,在C++中被称为二重调度问题,下面介绍几种方法解决二重调度问题。

1、虚函数+RTTI

虚函数实现了一个单一调度,我们只需要实现另一调度。其具体实现方法:将processCollision()定义为虚函数,解决一重调度,然后只需要检测一个对象类型,利用RTTI来检测对象的类型,再利用if…else语句来调用不同的处理方法。具体实现如下:

class GameObject{
public:
    virtual void collide(GameObject& otherObject) = 0;
    ...
};
class SpaceShip:public GameObject{
public:
    virtual void collide(GameObject& otherObject);
    ...
};

class CollisionWithUnknownObject{
public:
    CollisionWithUnknownObject(GameObject& whatWehit);
    ...
};
void SpaceShip::collide(GameObject& otherObject)
{
    const type_info& objectType = typeid(otherObject);
    if(objectType == typeid(SpaceShip))
    {
        SpaceShip& ss = static_cast<SpaceShip&>(otherObject);
        process a SpaceShip-SpaceShip collision;
    }
    else if(objectType == typeid(SpaceStation))
    {
        SpaceStation& ss = static_cast<SpaceStation&>(otherObject);
        process a SpaceShip-SpaceStation collision;
    }
    else if(objectType == typeid(Asteroid))
    {
        Asteroid& a = static_cast<Asteriod&>(otherObject);
        process a SpaceShip-Asteroid collision;
    }
    else 
    {
        throw CollisionWithUnknownObject(otherObject);
    }
}

该方法的实现简单,容易理解,其缺点是其扩展性不好。如果增加一个新的类时,我们必须更新每一个基于RTTI的if…else链,以处理这个新的类型。

2、只使用虚函数

基本原理就是用两个单一调度实现二重调度,也就是有两个单独的虚函数调用:第一次决定第一个对象的动态类型,第二次决定第二个对象动态类型。这种方法的缺陷仍然是:每个类必须知道它的所有同胞类,增加新类时,所有代码必须更新。
其具体实现如下:

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);
    ...
};

void SpaceShip::collide(GameObject& otherObject)
{
    otherObject.collide(*this);
}
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;
}

3、模拟虚函数表

在类外建立一张模拟虚函数表,该表是类型和函数指针的映射,加入新类型时不需改动其它类代码,只需在类外增加一个处理函数即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值