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。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。