virtual 函数以外的其它选择

复习effective C++ ,今天看到了"virtual 函数以外的其它选择",里面介绍了Strategy 模式的两种实现方式,也介绍了std::function 和 std::bind 函数等,结合这段时间学习的进行一个总结。

首先还是先来回顾书上的内容:

问题引入:

一个游戏需要对其中人们生命值,健康状况进行监控计算,因此需要定义一个专门的函数,但是不同的人物的计算方式是不同的,也就是说这个函数需要不同的实现方式。可以使用多态,这是最基本的方法。

实例:

class GameCharacter {

public:

virtual int healthValue() const;// return character's health rating

... // derived classes may redefine this

};

由于healthValue()没有声明为pure virtual , 这暗示我们将有个计算健康指数的缺省算法。这也可能成为一个弱点,在派生类中考虑重定义该函数的时候考虑不周。

因此,也可以使用“函数指针”作为成员变量,对于不同人物传给这个“函数指针”不同的计算函数。这就是:使用Function Pointer实现Strategy模式。

class GameCharacter;                       // forward declaration
    // function for the default health calculation algorithm
    int defaultHealthCalc(const GameCharacter& gc);
    class GameCharacter {
    public:
      typedef int (*HealthCalcFunc)(const GameCharacter&);
      explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf){}
      int healthValue() const
        { return healthFunc(*this); }
      ...
    private:
      HealthCalcFunc healthFunc;
    };
    //下面是同一类人中的不同实现:
    class EvilBadGuy: public GameCharacter {
   public:
    explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc) : GameCharacter(hcf)
      { ... }
    ...
  };
int loseHealthQuickly(const GameCharacter&);// health calculation
int loseHealthSlowly(const GameCharacter&);// funcs with different behavior

EvilBadGuy ebg1(loseHealthQuickly); //sametype charac-ters with
EvilBadGuy ebg2(loseHealthSlowly); //different  health-related behavior

从上面的代码可以看出,这样的代码具有很好的扩展性,虽然都是EvilBadGuy对象,但是通过绑定函数指针的方式可以使得实现方式灵活多变。

上面的:

typedef int (*HealthCalcFunc)(const GameCharacter&);

作用是定义 一个 返回值为 int 类型,以GameCharacter 的引用为参数的的函数的类型,以后用HealthCalcFunc 声明的函数都是这个类型。(记得以前竟然看不懂这种方式)。

也可以使用tr1::function 来完成Strategy模式(注:tr1::fucntion 现在已经是C++11 标准的一部分,存在于std namespace 中)

class GameCharacter;                                 // as before
    int defaultHealthCalc(const GameCharacter& gc);      // as before
    class GameCharacter {
      public:
      // HealthCalcFunc is any callable entity that can be called with
      // anything compatible with a GameCharacter and that returns 
      // anything compatible with an int; see below for details
      typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
      explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf){}
      int healthValue() const
      { return healthFunc(*this);   }
      ...
      private:
      HealthCalcFunc healthFunc;
    };

实现了上面的类之后,计算“健康状况”可以用下面的方式都行:

short   calcHealth(const  GameCharacter &);
    struct HealthCalculator {                     // class for health
      int operator()(const GameCharacter&) const   // calculation function
      { ... }                                                                        // objects
    };
    class GameLevel {
    public:
      float health(const GameCharacter&) const;      // health calculation
      ...                                            // mem function; note
    };                                            // non-int return type
    class EvilBadGuy: public GameCharacter {     // as before
    ...
    };
    class EyeCandyCharacter:   public GameCharacter {  // another character
    ...                                        // type; assume same
    };                                        //  constructor as EvilBadGuy
    EvilBadGuy ebg1(calcHealth);  // character using a
                                                                     // health calculation function
    EyeCandyCharacter ecc1(HealthCalculator());  // character using a
                                                                           // health calculation
                                                                           // function object
    GameLevel currentLevel;
    ...
    EvilBadGuy ebg2(                          // character using a
      std::tr1::bind(&GameLevel::health,        // health calculation
      currentLevel,                            // member function;
      _1)                                      // see below for details
    );

:上面的bind函数,有两个参数第一个是currentLevel 实际上是this指针,因为我们知道类的成员函数其实都有一个隐藏的参数 this 指针,在使用bind函数的时候需要指明该参数;后面的_1 叫做占位符,我们实际传进来的参数只有一个就用这个占位符代替。

下面来看一下Strategy 模式的经典实现方法:

virtual 函数以外的其它选择

这个图告诉你:GameCharacter是某个继承体系的根类,体系中的EvilBadGuyEyeCandyCharacter都是derived classesHealthCalcFunc是另一个继承体系的根类,体系中的SlowHealthLoserFastHealthLoser都是derived class,每个GameCharacter对象内含一个指针,指向一个来自HealthCalcFunc继承体系的对象。

class GameCharacter;                            // forward declaration
    class HealthCalcFunc {
    public:
      ...
      virtual int calc(const GameCharacter& gc) const
      { ... }
      ...
    };
    HealthCalcFunc defaultHealthCalc;
    class GameCharacter {
    public:
      explicit GameCharacter(HealthCalcFunc *phcf = &defaultHealthCalc) : pHealthCalc(phcf){}
      int healthValue() const
      { return pHealthCalc->calc(*this);}
      ...
    private:
      HealthCalcFunc *pHealthCalc;

    };

当你为解决问题而寻找某个设计方法时,不放考虑virtual函数的替代方案。一下有几个替代方案:

  1. 使用non-virtual interface (NVI)手法,它以public non-virtual成员函数包裹较低访问性(privateprotected)的virtual函数

  2. virtual函数替换为“函数指针成员变量”,这是Strategy设计模式的一部分表现形式

  3. 以“tr1 :: function”成员变量替换virtual函数,因而允许使用任何可调用实体搭配一个兼容于需求的签名式。

  4. 将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。这是Strategy设计模式的传统实现手法。

附:UML关系图的含义:

virtual 函数以外的其它选择

实际上,有两种不同的 has-a 关系。一个对象可以拥有另一个对象,其中被包含的对象是包含对象的一部分——或者不是。下图 中,我表示出 Airport拥 Aircraft。Aircraft 并不是 Airport 的一部分,但仍然可以说 Airport 拥有 Aircraft,这种关系称为聚集

virtual 函数以外的其它选择

另一种 has-a 关系是包含,被包含对象是包含对象的一部分,这种关系也称为组合。

下图显示了 Car(轿车)拥有 Tire(轮胎),后者是它的一部分(也就是说,Car 由 Tire 和其他东西组成),这种 has-a 关系,称为组合关系(composition),用实心菱形表示。此图上还显示了 Car 使用了 GasStation(加油站)类,这种使用关系用带箭头的虚线表示,也称依赖关系(dependencyrelationship)。

virtual 函数以外的其它选择

组合和聚集都有“一个对象包含一个或多个对象”的意思,但是,组合意味着“被包含对象是包含对象的一部分”,而聚集意味着被包含对象更像是一个集合。我们可以认为组合是一种非共享的关联,被包含对象的生存周期由包含对象控制。适当使用构造函数和析构函数在这里有助于对象的创建和销毁过程。
原文链接: https://www.cnblogs.com/zhuyp1015/archive/2012/08/12/2634316.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/59027

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午8:58
下一篇 2023年2月9日 上午8:58

相关推荐