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

时间: 2024-10-28 10:05:38

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

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

1. 在一个继承层次中,不同的类要实现同一接口的不同实现,最先想到的可能是虚函数,假设一个存在一个继承体系,这个集成体系中的每一层都需要一个名为fun函数,那么可能会像这样实现: clase Base{ public: ... virtual fun(int num){...} private: ... } class Derived:public Base{ public: ... virtual fun(int num){} private: ... } 但除了将fun设为虚函数,还有其他选

Effective C++笔记_条款43 学习处理模板化基类内的名称

开篇就来了一个示例代码,整个这个小节就围绕这个示例代码来描述模板化基类内的名称(函数).主要是因为C++知道base class templates有可能被特化,而那个特化版本肯呢个不提供和一般性template相同的接口.因此它往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称. 1 class CompanyA { 2 public: 3 //... 4 void sendCleartext(const std::string& msg); 5 vo

Effective C++笔记_条款46 需要类型转换时请为模板定义非成员函数

看这个章节的时候又跑回去看了一下 条款 24,本章的例子就是基于下面这个例子而来的.在 item 24 中,支持混合运算的示例代码如下: 1 class Rational { 2 public: 3 Rational(int numerator = 0, int denominator = 1); 4 int mumerator() const; 5 int denominator() const; 6 private: 7 int numerator; 8 int denominator; 9

Effective C++笔记_条款25考虑写出一个不抛出异常的swap函数

1 // lib中的swap 2 namespace std { 3 template<typename T> 4 void swap (T& a, T& b) 5 { 6 T temp(a); 7 a = b; 8 b = temp; 9 } 10 } 11 12 // 缺点:需要赋值大量的数据,但是有的时候并不要复制如此多的内容 13 class WidgetImpl { 14 public: 15 //... 16 private: 17 int a, b, c; 18

《Effective C++》:条款35:考虑virtual函数以外的其他选择

条款35:考虑virtual函数以外的其他选择 条款35考虑virtual函数以外的其他选择 藉由Non-virtual Interface手法实现Template Method模式 藉由Function Pointers实现Strategy模式 藉由tr1function完成Strategy模式 古典的Strategy模式 摘要 virtual函数在派生中经常用到,在遇到一些问题时用virtual函数没问题,但是有时候我们应该思考一下是否有替代方案,以此来拓宽我们的视野. 假如现在正在写一个游

Effective C++_笔记_条款07_为多态基类声明virtual析构函数

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 这个规则只适用于polymorphic(带多态性质的)base class身上.这种base class的设计目的是为了用来“通过base class接口处理derived class对象”.假如我们在程序中设计factory(工厂)函数,让它返回base class指针,指向新生成的derived class对象,假设base class有个non-virtu

Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为方便采用书上的例子,先提出问题,在说解决方案. 1 问题 1: class Transaction{ 2: public: 3: Transaction(); 4: virtual void LogTransaction() const = 0 ; 5: ... 6: }; 7:  8: Transaction::Transaction() //Base cl

Effective C++ 阅读笔记_条款27 尽量少做转型动作

Effective C++ 阅读笔记_条款27 尽量少做转型动作 1.转型的三种形式,可以分为两大类. (1)旧式转型(old-style casts) (1.1) (T) expresstion (1.2) T (expression) (2) 新式转型(c++-style casts) (2.1)const_cast<T> (expression) (2.2)dynamic_cast<T> (expression) (2.3)reinterpret_cast<T>

Effective C++_笔记_条款08_别让异常逃离析构函数

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++并不禁止析构函数吐出异常,但它不鼓励你这样做.考虑如下代码: 1: class Widget{ 2: public: 3: ... 4: ~Widget() {...} //假设这个可能吐出一个异常 5: }; 6:  7: void doSomething() 8: { 9: vector<Widget> v ; //v在这里被自动销毁 10: ...