Effective C++读书笔记(条款35-40)
来源:互联网 发布:农村淘宝报名 编辑:程序博客网 时间:2024/06/05 21:51
(六).继承与面向对象设计
____________________________________________________________________________________________________________________________________
条款35:考虑virtual函数以外的其他选择
#1.virual函数的四个替代方案:(1).使用non-virtual interface(NVI)手法,那是 Template Method 设计模式
的一种特殊形式。它以public non-virtual 成员函数包裹较低访问性(private
或 protected)的virtual函数。
(2).将virtual函数替换为 "函数指针成员变量", 这是Strategy设计模式的一种
分解表现方式。
(3).以tr1::function 成员变量替换 virtual 函数,因而允许使用任何可调用物
(callable entity) 搭配一个兼容于需求的签名式。这也是 Strategy设计模式的
某种形式。
(4).将继承体系内的 virtual 函数替换为另一个继承体系内的virtual 函数。
这是Strategy 设计模式的传统实现手法。
<1>.由Non-Virtual Interface手法实现Template Method模式
//考虑以下代码:class GameCharacter{public: int healthValue() const //derived classes不重新定义它 { //见36条款 ... //做一些事前工作,详下 int retVal = doHealthValue(); //做真正的工作 ... //做一些事后工作 } ...private: virtual int doHealthValue() const //derived classes可重新定义它 { ... }};//我们称healthValue函数为virutal函数doHealthValue的外覆器,//它提供了完整实现,而virtual函数则提供了具体实现,//通过NVI手法,我们可以更专注于事件如何被具体完成
<2>.由Function Pointers 实现 Strategy 模式
//考虑以下代码: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&); //健康函数计算函数1int loseHealthSlowly(const GameCharacter&); //健康函数计算函数2EvilBadGuy ebg1(loseHealthQuickly); //相同类型的人物搭配EvilBadGuy ebg2(loseHealthSlowly); //不同的健康计算方式//defualtHealthCalc并未访问EvilBadGuy的non-public成分,//若要访问non-public成分,需使其成为friends,因此可能//降低class的封装性。//运用函数指针替换virtual函数,其优点(像是"每个对象//可各自拥有自己的健康计算函数”和“可在运行期改变计算//函数”)是否足以弥补缺点(例如可能降低GameCharacter//的封装性),是你必须根据设计情况而抉择的。
<3>.由tr1::function完成Strategy模式
//考虑以下代码: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-intstruct 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的好处是允许使用任何可调用物,//只要其调用形式兼容于签名式
<4>.由古典的 Strategy 模式
//考虑以下代码: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 即可。____________________________________________________________________________________________________________________________________
条款36:绝不重新定义继承而来的non-virtual函数
#1.绝不重新定义继承而来的non-virtual函数
理由(1):non-virtual及重新定义下的函数都是静态绑定,
调用它们的结果不是我们所预期的。
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意味着
其实现容易被替换,因此实现动态绑定,而non-virtual意味着其不变性
凌驾于特异性,因继承其实现所以应用于静态绑定。
____________________________________________________________________________________________________________________________________
条款37:绝不重新定义继承而来的缺省参数值
#1.绝不重新定义继承而来的缺省参数值,因为缺省参数值都是静态绑定,
而virtual 函数---你唯一应该覆写的东西---却是动态绑定。
//一个用以描述几何形状的classclass 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.____________________________________________________________________________________________________________________________________
条款38:通过复合塑模出 has-a 或 “根据某物实现出”
#1.复合有双层含义,在应用域(application domain), 复合意味 has-a
(有一个)。在实现域(implementation domain), 复合意味
is-implemented-in-terms-of (根据某物实现出)。
____________________________________________________________________________________________________________________________________
条款39:明智而审慎地使用private继承
#1.Private 继承意味 is-implemented-terms-of(根据某物实现出)。它
通常比复合(composition)的级别低。但是当 derived class需要访问
protected base class 的成员,或需要重新定义继承而来的 virtual 函数
时,这么设计是合理的。
//例如我们要使用Timer中的功能,我们可以implemented-in-terms-ofclass widget:private Timer{private: //private权限:避免客户调用 virtual void onTick() const; //重新定义我们所需的onTick函数功能 ...};
#2.一种替代private继承的设计是使用复合class,其用法如下:
class Widget{private: class WidgetTimer:public Timer{ public: virtual void onTick() const; ... }; WidgetTimer timer; ...};//这种设计的好处是://(1).它可以拥有自己的derived class.//(2).将编译依存性降至最低(分离,相依于声明式)
#3.独立非附属对象的大小一定不为零,但作为附属对象,它存在
EBO(Empty base optimization),即空白基类最优化,可以使其
大小为零。
例如:
class Empty{};class HoldsAnInt:private Empty{private: int x;}//几乎可以确定,sizeof(HoldsAnInt) = sizeof(int)
#4.和复合(compresition)不同,private继承可以造成empty base最优化。
这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要,此时
#2的替代设计就不能再满足了。
____________________________________________________________________________________________________________________________________
条款40:明智而审慎地使用多重继承
#1.virtual 继承会增加大小,速度,初始化等成本,因此非必要时不要使用
virtual继承。同时它也意味着,virtual base class 数据越少,其使用
价值越高,因此应尽可能避免在其中放置数据。
#2.当涉及继承组合时,多重继承便发挥了其正当用途。例如"public继承某个
Interface class" 和 “private 继承某个协助实现的 class" 的两两组合:
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 ”界限函数“};
____________________________________________________________________________________________________________________________________
1 0
- 【Effective C++】读书笔记 条款13~条款17
- Effective C++读书笔记(条款35-40)
- 【Effective C++】读书笔记 条款49~51
- 【Effection C++】读书笔记 条款34~条款35
- 读书笔记《Effective C++》条款35:考虑virtual函数以外的其他选择
- 读书笔记《Effective C++》条款40:明智而审慎地使用多重继承
- 读书笔记: effective C++; 条款22 和 23, 尽量用传址少用传值。
- 读书笔记《Effective C++》条款03:尽可能使用const
- 读书笔记《Effective C++》条款13:以对象管理资源
- 读书笔记《Effective C++》条款19:设计class犹如设计type
- 读书笔记《Effective C++》条款27:尽量少做转型动作
- 读书笔记《Effective C++》条款30:透彻了解inlining的里里外外
- 读书笔记《Effective C++》条款42:了解typename的双重意义
- Effective C++ 读书笔记 条款一至三
- Effective C++ 读书笔记 条款01
- effective C++ 读书笔记 条款20
- effective C++ 读书笔记 条款21
- effective C++ 读书笔记 条款06
- 深入理解计算机网络学习笔记--TCP
- 支付宝红包首秀失利
- 教你用Django实现一个简单的GIS功能
- Linux平台编译curl7.37.1(32位、64位)
- Android图片优化
- Effective C++读书笔记(条款35-40)
- jquery学习之基础核心
- sqlldr导入数据---笔记
- poj 1062 昂贵的聘礼 [dijkstra]
- android设置背景色为透明
- 框架和平台
- ListView的动态刷新问题——用notifyDataSetChanged没作用
- onCreate中的savedInstanceState有何具体作用
- Inno Setup 安装脚本的初级应用