条款35:考虑virtual函数以外的其他选择
有一部分人总是主张virtual函数几乎总应该是private:例如下面这个例子,例子时候游戏,游戏里面的任务都拥有健康值这一属性:
class GameCharacter{ public: int healthValue()const{ ... int retVal = doHealthValue(); ... return retVal; } private: virtual int doHealthValue() const { }//上面的这个non-virtual函数实际上是virtual函数的一个外覆器 };
这里使用一个外覆器的好处是:了一使用外覆器在调用虚函数之前以及之后分别做一些特定的操作。例如锁定mutex或者是解锁mutex这种事。
上面这个可以称为Template Method模式的一种特例:
还有用function Pointer实现的strategy模式;假如healthValue的规则有一个内含的成员healthFunc来进行指定(这样降低了耦合)
1 class GameCharacter; 2 int defaultHealthCalc(const GameCharacter & gc); 3 class GameCharacter{ 4 public: 5 typedef std::function<int (const GameCharacter & )> HealCalcFunc 6 explicit GameCharacter(HealCalcFunc hcf = defaultHealthCalc) 7 :healthFunc(hcf){} 8 int healthValue() const 9 {return healthFunc(* this); } 10 ... 11 private: 12 HealCalcFunc healthFunc; 13 };
实际上,这个typedef带来的弹性是很大的:
1 short clacHealth(const GameCharacter &); 2 //或者是一个函数对象 3 struct HealCalculater{ 4 public: 5 float operator()(const GameCharacters & gc) const ; 6 }; 7 //或者是一个成员函数。 8 class GameLevel{ 9 public: 10 float Health(const GameCharacters)const; 11 }; 12 GameCharacters character1(clacHealth); 13 GameCharacters character2(HealCalculater()); 14 GameLevel levelObj; 15 GameCharacters character3(std::bind(&GameLevel::health, levelObj, _1));//不记得这样写对不对,待查
上面这些例子都可以正确通过切运行,这说明这种方式给我们带来的弹性是很大的。
不仅如此,这种方式在延伸一下就可以设计出来典型的策略模式:
1 class HealCalculater; 2 class GameCharacter; 3 int defaultHealthCalc(const GameCharacter & gc); 4 class GameCharacter{ 5 public: 6 typedef std::function<int (const GameCharacter & )> HealCalcFunc 7 explicit GameCharacter(HealCalcFunc hcf = defaultHealthCalc) 8 :healthFunc(hcf){} 9 int healthValue() const 10 {return healthFunc(* this); } 11 ... 12 private: 13 HealCalcFunc * phealthFunc; 14 }; 15 HealCalculater{ 16 public : 17 virtual int calc(const GameCharacter & gc)const 18 {} //也可以只声明一个接口 19 }
小结:
virtual函数的替代方案包括nv1手法以及strategy设计模式等多种形式
将机能从成员函数移动到class外部函数,带来的缺点是非成员函数无法访问class的non-public成员
注意function已经bind的使用