zoukankan      html  css  js  c++  java
  • 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 模式的经典实现方法:

      这个图告诉你:GameCharacter 是某个继承体系的根类,体系中的EvilBadGuyEyeCandyCharacter 都是derived classes HealthCalcFunc 是另一个继承体系的根类,体系中的SlowHealthLoser FastHealthLoser 都是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函数
    1. virtual函数替换为“函数指针成员变量”,这是Strategy设计模式的一部分表现形式
    2. 以“tr1 :: function”成员变量替换virtual函数,因而允许使用任何可调用实体搭配一个兼容于需求的签名式。
    1. 将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。这是Strategy设计模式的传统实现手法。

    附:UML关系图的含义:

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

     

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

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

      组合和聚集都有“一个对象包含一个或多个对象”的意思,但是,组合意味着“被包含对象是包含对象的一部分”,而聚集意味着被包含对象更像是一个集合。我们可以认为组合是一种非共享的关联,被包含对象的生存周期由包含对象控制。适当使用构造函数和析构函数在这里有助于对象的创建和销毁过程。

  • 相关阅读:
    博客样式备份
    2018年终总结
    技术博客的太监
    LeetCode 日常填坑
    互联网之父
    TotoiseSVN的使用方法
    常用CMD命令
    量化策略
    浏览器加载js的阻塞与非阻塞
    Vue核心之数据劫持
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2634316.html
Copyright © 2011-2022 走看看