Effective C++读书笔记10(35~36)

条款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;
};

拿他和内置于GameCharacter继承体系内的virtual函数比较,它提供了某些有趣的弹性:

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());

2.2 某已知人物之健康指数计算函数可在运行期改变,例如GameCharacter可提供成员函数setHealthCharacter,用来替换当前的健康指数计算函数。


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;
};

和前边唯一不同的是如今GameCharacter持有一个tr1::function对象,相当于一个指向函数的泛化指针,现在来看看它的惊人威力:

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);
}

4.古典的Strategy模式


假设条款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函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值