• Effective C++笔记_条款35 考虑virtual 函数以外的其他选择


        因为其他的事情耽误了看书,现在将看的笔记记录下来。

    1 class GameCharacter {
    2  public:
    3      virtual int healthValue() const;
    4  };

    1. 藉由Non-Virtual Interface 手法实现 Template Method模式
    (1)non-virtual interface(NVI):
      令客户通过public non-virtual 成员函数间接调用private virtual函数
    (2) virtual 函数的外覆器(wrapper):non-virtual 函数

     1 class GameCharacter {
     2  public:
     3      int healthValue() const    // virtual 函数的外覆器(wrapper)
     4      {
     5          //...
     6          /*
     7           做些事前工作:锁定互斥器,制造运转日志记录项,验证class约束条件、验证函数先决条件等等
     8          */
     9          int retVal = doHealthValue();
    10          //...
    11          /*
    12          做些事后工作:互斥器解除锁定,验证函数的时候条件、再次验证class约束条件等等。
    13          */
    14          return retVal;
    15      }
    16  private:
    17      virtual int doHealthValue() const
    18      {
    19          //...
    20      }
    21  };

    优点:
      先比于直接调用virtual 函数,就没有任何好办法可以处理事前和时候工作。
    注意事项:
      重新定义virtual 函数,表示某些事“如何”被完成,“调用virtual 函数”则表示它“何时”被完成。

      NVI手法允许derived classs重新定义virtual函数,从而赋予他们“如何实现机能”的控制能力,但base class保留诉说“函数何时被调用”的权利。


    2.藉由Function Pointers 实现Strategy模式

     1 class GameCharacter;    // forward declaration
     2 
     3  int defaultHealthCalc(const GameCharacter& gc);    /*计算健康指数的缺省算法*/
     4  class GameCharacter {
     5  public:
     6          typedef int (*HealthCalcFunc) (const GameCharacter&);
     7          explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
     8              :healthFunc(hcf)
     9          {}
    10 
    11          int healthValue() const
    12          {
    13              return healthFunc(*this);
    14          }
    15  private:
    16      HealthCalcFunc healthFunc;
    17  };

    上述模式的解释说明:
    (1)同一任务类型的不同实体可以不同的健康计算函数。e.g:

     1 class EvilBadGuy:public GameCharacter {
     2 public:
     3     explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)
     4         :GameCharacter(hcf)
     5     {
     6         //...
     7     }
     8 };
     9 
    10 int loseHealthQuickly(const GameCharacter&);
    11 int loseHealthSlowly(const GameCharacter&);
    12 
    13 /* 相同类型的的人物搭配不同的健康计算方式 */
    14 EvilBadGuy ebg1(loseHealthQuickly);
    15 EvilBadGuy ebg2(loseHealthSlowly);

    (2)某已知人物的健康指数计算函数可在运行期变更。

      例如GameCharacter提供一个setHealthCalculator函数,用来替换当前的健康指数计算函数。

    (3)一般而言,唯一能够解决“需要以non-member函数访问class的non-public成分”的办法是:弱化class的封装。例如,class可声明那个non-member函数为friends或为其实现的某一部分提供public访问函数。
     ----> 藉由Function Pointers 实现Strategy模式的缺点是:弱化了class的封装性。



     3.藉由tr1::function完成Strategy模式

     1 class GameCharacter;
     2  int defaultHealthCalc(const GameCharacter& gc);
     3  class GameCharacter {
     4  public:
     5          /*
     6          HealthCalcFunc 可以是任何“可调用物”(callable entity),可被调用并接受任何兼容于
     7          GameCharacter的事物(参数可被隐式转换为const GameCharacter&),
     8          返回任何兼容于int的东西(可被隐式转换为int)。
     9 
    10          类似一个指向函数的泛化指针
    11          */
    12          typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
    13          explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
    14              :healthFunc(hcf)
    15          {}
    16 
    17          int healthValue() const
    18          {
    19              return healthFunc(*this);
    20          }
    21  private:
    22      HealthCalcFunc healthFunc;
    23 
    24  };
    25 
    26 /*健康计算函数,注意其返回类型为non-int*/
    27  short calcHealth(const GameCharacter&); 
    28 
    29 /*为计算健康而设计的函数对象*/
    30  struct HealthCalculator {
    31      int operator() (const GameCharacter&) const
    32      {
    33          //...
    34      }
    35  };
    36 
    37  class GameLevel {
    38  public:
    39      /*成员函数,用以计算健康;注意其non-int返回类型*/
    40      float health(const GameCharacter&) const;
    41      //...
    42  };
    43 
    44 class EvilBadGuy:public GameCharacter {
    45 public:
    46     explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)
    47         :GameCharacter(hcf)
    48     {
    49         //...
    50     }
    51 };
    52 
    53 class EyeCandyCharacter:public GameCharacter {
    54 public:
    55     explicit EyeCandyCharacter(HealthCalcFunc hcf = defaultHealthCalc)
    56         :GameCharacter(hcf)
    57     {
    58         //...
    59     }
    60 };
    61 
    62 /*使用函数计算健康指数*/
    63 EvilBadGuy ebg1(calcHealth); 
    64 /*使用某个函数对象计算健康指数*/ 
    65 EyeCandyCharacter eccl(HealthCalculator());    
    66 
    67 /*使用某个成员函数计算健康指数*/
    68 GameLevel currentLevel;
    69 EvilBadGuy ebg2(std::tr1::bind(&GameLevel::health, currentLevel, _l));

    4.古典的Strategy模式
      传统的Strategy做法会将健康计算函数做成一个分离的继承体系中的virtual成员函数

     1 class GameCharacter;
     2 class HealthCalcFunc {
     3 public:
     4     //...
     5     virtual int calc(const GameCharacter& gc) const
     6     {
     7         //...
     8     }
     9     //...
    10 };
    11 
    12 HealthCalcFunc defaultHealthCalc;
    13 class GameCharacter {
    14 public:
    15     explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc)
    16         :pHealthCalc(phcf)
    17     {}
    18 
    19     int healthValue() const
    20     {
    21         return pHealthCalc->calc(*this);
    22     }
    23 
    24     //...
    25 private:
    26     HealthCalcFunc *pHealthCalc;
    27 };

    5.总结

      当你为解决问题而寻找某个设计方法时,不妨考虑virtual函数的替代方案。
      (1)使用non-vvirtual interface(NVI)手法,它以public non-virtual 成员函数包裹较低
        访问性(private或protected)的virtual函数。

      (2)将virtual函数替换为“函数指针成员变量”。

      (3)以tr1::function 成员变量替换virtual,因而允许使用任何可调用物搭配一个兼容与需求的签名式。

      (4)将继承体系内的virtual函数替换为另一个继承体系的virtual函数。


    注明:全文文字都是来源《Effective C++》 3th。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

  • 相关阅读:
    剑指offer二十二之从上往下打印二叉树
    剑指offer二十一之栈的压入、弹出序列
    Hadoop简介与伪分布式搭建—DAY01
    getopt解析命令行参数一例:汇集多个服务器的日志
    软件开发:如何表达和维护大型逻辑
    编程语言与可复用性
    危险的 SQL
    谁终将点燃闪电,必长久如云漂泊
    如何使错误日志更加方便排查问题
    生活的诀窍:任务激励式学习法和短小目标法
  • 原文地址:https://www.cnblogs.com/cloudfeng/p/4380253.html
Copyright © 2020-2023  润新知