标签:c++ effective c++ 语法优化
(六).继承与面向对象设计
____________________________________________________________________________________________________________________________________
条款35:考虑virtual函数以外的其他选择
#1.virual函数的四个替代方案://考虑以下代码:
class GameCharacter{
public:
int healthValue() const //derived classes不重新定义它
{ //见36条款
... //做一些事前工作,详下
int retVal = doHealthValue(); //做真正的工作
... //做一些事后工作
}
...
private:
virtual int doHealthValue() const //derived classes可重新定义它
{
...
}
};
//我们称healthValue函数为virutal函数doHealthValue的外覆器,
//它提供了完整实现,而virtual函数则提供了具体实现,
//通过NVI手法,我们可以更专注于事件如何被具体完成//考虑以下代码:
class GameCharacter; //前置声明(forward declaration)
//以下函数是计算健康指数的缺省算法
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;
};
//同一人物类型之不同实体可以有不同的健康计算函数,例如:
class EvilBadGuy:public GameCharacter{
public:
explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)
: GameCharacter(hcf)
{...}
...
};
int loseHealthQuickly(const GameCharacter&); //健康函数计算函数1
int loseHealthSlowly(const GameCharacter&); //健康函数计算函数2
EvilBadGuy ebg1(loseHealthQuickly); //相同类型的人物搭配
EvilBadGuy ebg2(loseHealthSlowly); //不同的健康计算方式
//defualtHealthCalc并未访问EvilBadGuy的non-public成分,
//若要访问non-public成分,需使其成为friends,因此可能
//降低class的封装性。
//运用函数指针替换virtual函数,其优点(像是"每个对象
//可各自拥有自己的健康计算函数”和“可在运行期改变计算
//函数”)是否足以弥补缺点(例如可能降低GameCharacter
//的封装性),是你必须根据设计情况而抉择的。//考虑以下代码:
class GameCharacter;
int defaultHealthCalc(const GameCharacter&);
class GameCharacter{
public:
//HealthCalcFunc可以是任何 “可调用物” (callable entity),
//可被调用并接受任何兼容于 GameCharacter 之物,返回任何
//兼容于 int 的东西。详下。
typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
: healthFunc(hcf)
{}
int healthValue() const {return healthFunc(*this);}
...
private:
HealthCalcFunc healthFunc;
};
//如今的tr1::function相当于一个指向函数的泛化指针,
//它在“指定健康计算函数”这件事上具有更惊人的弹性:
short calcHealth(const GameCharacter&); //健康计算函数
//注意其返回类型为non-int
struct healthCalculator{ //为计算健康而设计的函数对象
int operator()(const GameCharacter&) const
{...}
};
class GameLevel{
public:
float health(const GameCharacter&)const; //成员函数,用以计算健康;
... //注意其non-int 返回类型
};
class EvilBadGuy:public GameCharacter{ //同前
...
};
class EyeCandyCharacter:public GameCharacter{ //另一个人物类型;
... //假设其构造函数与
}; //EvilBadGuy相同
EvilBadGuy ebg1(calcHealth); //人物1,使用某个
//函数计算健康函数
EveCandyCharacter ecc1(HealthCalculator()); //人物2,使用某个
//函数对象计算健康函数
GameLevel currentLevel;
...
EvilBadGuy ebg2( //函数3,使用某个
std::tr1::bind(&GameLevel::health, //成员函数计算健康函数
currentLevel,
_1,) //详见以下
);
//使用tr1::function的好处是允许使用任何可调用物,
//只要其调用形式兼容于签名式//考虑以下代码:
class GameCharacter;
class HealthCalcFunc{
public:
...
virtual int calc(const GameCharacter& gc) const
{ ... }
...
};
HealthCalcFunc defaultHealthCalc;
class GameCharacter{
public:
explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc)
: pHealthCalc(phcf)
{}
int healthValue() const
{ return pHealthCalc->calc(*this); }
...
private:
HealthCalcFunc* pHealthCalc;
};
//这个解法的吸引力在于,它提供“将一个既有的健康算法纳入
//使用”的可能性---只要为HealthCalcFunc 继承体系添加一个
//derived class 即可。____________________________________________________________________________________________________________________________________class B{
public:
void mf();
...
};
class D:public B {
public:
void mf();
...
};
D x;
B* pB = &x;
D* pD = &x;
pB->mf(); //调用B::mf()
pD->mf(); //调用D::mf()
//对于调用同一个对象x,却出现了不同行为,
//这不是我们所希望的,与其如此重新定义,
//还不如让其成为virtual函数理由(2).继承下来的函数分为virtual和non-virtual, virtual意味着//一个用以描述几何形状的class
class Shape{
public:
enum ShapeColor { Red, Green, Blue };
//所有形状都必须提供一个函数,用来描绘自己
virtual void draw(ShapeColor color = Red) const = 0;
...
};
class Rectangle:public Shape{
public:
//注意,赋予不同的缺省参数值。真实糟糕!
virtual void draw(ShapeColor color = Green) const;
...
};
class Circle:public Shape{
public:
virtual void draw(ShapeColor color) const
//请注意,以上这么写则当客户以对象调用此函数,一定要指定参数值。
//因为静态绑定下这个函数并不从其base 继承缺省参数值。
//但若以指针(或reference) 调用此函数,可以不指定其参数值,
//因为动态绑定下这个函数会从其base继承缺省参数值
...
};
//现考虑以下指针:
Shape* ps; //静态类型为Shape*
Shape* pc = new Circle; //静态类型为Shape*
Shape* pr = new Rectangle; //静态类型为Shape*
pc->draw(Shape::Red); //调用Circle::draw(Shape::Red)
pr->draw(Shape::Red); //调用Rectangle::draw(Shape::Red)
//此代码的virtual函数是动态绑定,而缺省参数值却是静态绑定
//这造成了一种奇怪的调用方式,不能统一调用,而编译器之所以
//不为缺省参数值动态绑定的原因是运行期效率。
//那么我们是否可以为derived指定同样的缺省值呢?,就像这样:
class Rectangle:public Shape{
public:
virtual void draw(ShapeColor color = Red) const;
...
};
//答案是否,理由很简单,这造成了我们的代码重复,
//而且带有相依性,要是哪天修改了缺省值就要动辄牵动全身了。
//一种更好的做法是让non-virtual指定缺省值来代替工作:
class Shape{
public:
enum draw(ShapeColor color = Red) const //如今它是一个non-virtual
{
doDraw(color); //调用一个virtual
}
...
private:
virtual void doDraw(ShapeColor color)const = 0; //真正的工作在此处完成
};
class Rectangle:public Shape{
public:
...
private:
virtual void doDraw(ShapeColor color)const; //无须指定缺省值
...
};
//由于non-virtual函数绝不被derived class覆写,这个设计很清楚地使得
//draw 函数的color 缺省参数值总是为 true.____________________________________________________________________________________________________________________________________//例如我们要使用Timer中的功能,我们可以implemented-in-terms-of
class widget:private Timer{
private: //private权限:避免客户调用
virtual void onTick() const; //重新定义我们所需的onTick函数功能
...
};class Widget{
private:
class WidgetTimer:public Timer{
public:
virtual void onTick() const;
...
};
WidgetTimer timer;
...
};
//这种设计的好处是:
//(1).它可以拥有自己的derived class.
//(2).将编译依存性降至最低(分离,相依于声明式)class Empty{};
class HoldsAnInt:private Empty{
private:
int x;
}
//几乎可以确定,sizeof(HoldsAnInt) = sizeof(int)class IPerson{ //该class指出需实现接口
public:
virtual ~IPerson();
virtual std::string name() const = 0;
virtual std::string birthDate() const = 0;
virtual std::string birthDate() const = 0;
};
class DatebaseID{...}; //稍后被使用,细节不重要。
class PersonInfo{ //这个class有若干有用函数
public: //可用以实现IPerson接口。
explicit PersonInfo(DatabaseID pid);
virtual ~PersonInfo(();
virtual const char* theName() const;
virtual const char* theBirthDate() const;
virtual const char* valueDelimOpen() const;
virtual const char* valuedelimClose() const;
...
};
class CPerson:public IPerson, private PersonInfo{ //注意,多重继承
public:
explicit CPerson(DatabaseID pid):PersonInfo(pid){}
virtual std::string name() const //实现必要的IPerson函数
{return PersonInfo::theName();}
virtual std::string birthDate() const //实现必要的IPerson函数
{return PersonInfo::theBirthDate();}
private:
const char* valueDelimOpen() const {return "";} //重新定义继承而来的
const char* valueDelimClose() const {return "";}//virtual ”界限函数“
};
____________________________________________________________________________________________________________________________________
标签:c++ effective c++ 语法优化
原文地址:http://blog.csdn.net/beyond_ray/article/details/43792291