条款35:考虑virtual函数以外的其他选择
设计一款游戏,为不同的人物设计不同的计算健康指数的函数,那么使用virutal函数很自然:
class GameCharacter{
public:
virtual int healthValue() const;
//...
};
现在来考虑virtual函数的替代方案。
1.藉由Non-virtual interface手法实现Template Method模式
2.藉由Function Pointers实现Strategy模式
该方法主张:人物健康指数的计算与人物类型无关,这样的计算完全不需要人物这个成分:
class GameCharacter; 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; };
2.1 同一人物类型之不同实体可以有不同的健康计算函数,如:
class EvilBadGuy: public GameCharacter{ public: explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc) : GameCharacter(hcf){ } //... }; int loseHealthQuckly(const GameCharacter&); int loseHealthSlowly(const GameCharacter&); EvilBadGuy ebg1(loseHealthQuckly); EvilBadGuy ebg2(loseHealthSlowly());
3.藉由tr1::function完成Strategy模式
一旦习惯了template以及它对隐式接口(条款41)的使用,基于函数指针的做法看起来便过分死板了。为什么要求健康指数计算必须是个函数,而不能使某种像函数的东西,例如函数对象?如果一定得是函数,为什么不能够是个成员函数?为什么一定得返回int而不是任何可被转换为int的类型呢?
来看tr1::function的使用:
tr1::function: class GameCharacter; int defaultHealthCalc(const GameCharacter& gc); class GameCharacter{ public: typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc; explict GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) :healthFunc(hcf) {} int healthValue() const{ return healthFunc(*this); } private: HealthCalcFunc healthFunc; };
short calcHealth(const GameCharacter&); struct HealthCalculator{ int operator()(const GameCharacter&) const{ //... } }; class GameLevel{ public: float health(const GameCharacter&) const; //... }; class EvilBadGuy: public GameCharacter{ }; class EyeCandyCharacter: public GameCharacter{ //... }; EvilBadGuy ebg1(calcHealth); EyeCandyCharacter ecc1(HealthCalculator()); GameLevel currentLevel; EvilBadGuy ebg2{ std::tr1::bind(&GemeLevel::health, currentLevel, _1); }
假设条款36:绝不重新定义继承而来的non-virtual函数
类D由类B以public形式派生而来,B定义了一个public成员函数mf,由于mf的参数和返回值都不重要,所以我假设两者皆为void:
class B{ public: void mf(); //... }; class D: public B{}; D x; //if we do this: B* pB = &x; pb->mf(); //it is different from this: D* pD = &x; pD->mf()你可能会感到惊讶。毕竟两者都通过对象x调用成员函数mf,由于两者所调用的函数都相同,凭借的对象也相同,所以行为也应该相同,是吗?
事实并非如此,如果mf是个non-virtual函数而D定义有自己的mf版本,那就不是如此:
class D: public B{ public: void mf(); //... }; pB->mf(); //调用B::mf pD->mf(); //调用D::mf原因是:non-virtual函数如B::mf和D::mf都是静态绑定,意思是:由于pB被声明为一个pointer-to-B,通过non-virtual函数永远是B所定义的版本。
如果你正在编写class D并重新定义继承自class B的non-virutal函数,D对象很可能展现出精神分裂的不一致行径:当mf被调用,任何一个D对象都可能表现出B或者D的行为,决定因素不在对象自身,而在于指向对象之指针当初的声明类型。
以下没看懂,不抄了。。。
请记住:
绝对不要重新定义继承而来的non-virtual函数