分享
 
 
 

翻译:Effective C++, 3rd Edition, Item 35: 考虑可选的 virtual functions(虚拟函数)的替代方法(上)

王朝c/c++·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

Item 35: 考虑可选的 virtual functions(虚拟函数)的替代方法

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.csdn.net/fatalerror99/

现在你工作在一个视频游戏上,你在游戏中为角色设计了一个 hierarchy(继承体系)。你的游戏中有着变化多端的恶劣环境,角色被伤害或者其它的健康状态降低的情况并不罕见。因此你决定提供一个 member function(成员函数)healthValue,它返回一个象征角色健康状况如何的整数。因为不同的角色计算健康值的方法可能不同,将 healthValue 声明为 virtual(虚拟)似乎是显而易见的设计选择:

class GameCharacter {

public:

virtual int healthValue() const; // return character's health rating;

... // derived classes may redefine this

};

healthValue 没有被声明为 pure virtual(纯虚)的事实暗示这里有一个计算健康值的缺省算法(参见 Item 34)。

这确实是一个显而易见的设计选择,而在某种意义上,这是它的缺点。因为这样的设计过于显而易见,你可能不会对它的其它可选方法给予足够的关注。为了帮助你脱离 object-oriented design(面向对象设计)的习惯性道路,我们来考虑一些处理这个问题的其它方法。

The Template Method Pattern via the Non-Virtual Interface Idiom(经由非虚拟接口惯用法实现的模板方法模式)

我们以一个主张 virtual functions(虚拟函数)应该几乎总是为 private(私有的)的有趣观点开始。这一观点的拥护者提出:一个较好的设计应该保留作为 public member function(公有成员函数)的 healthValue,但应将它改为 non-virtual(非虚拟的)并让它调用一个 private virtual function(私有虚拟函数)来做真正的工作,也就是说,doHealthValue:

class GameCharacter {

public:

int healthValue() const // derived classes do not redefine

{ // this - see Item 36

... // do "before" stuff - see below

int retVal = doHealthValue(); // do the real work

... // do "after" stuff - see below

return retVal;

}

...

private:

virtual int doHealthValue() const // derived classes may redefine this

{

... // default algorithm for calculating

} // character's health

};

在这个代码(以及本 Item 的其它代码)中,我在类定义中展示 member functions(成员函数)的本体。就像 Item 30 中所解释的,这会将它们隐式声明为 inline(内联)。我用这种方法展示代码仅仅是这样更易于看到它在做些什么。我所描述的设计与是否 inline 化无关,所以不必深究 member functions(成员函数)定义在类的内部有什么意味深长的含义。根本没有。

这个基本的设计——让客户通过 public non-virtual member functions(公有非虚拟成员函数)调用 private virtual functions(私有虚拟函数)——被称为 non-virtual interface (NVI) idiom(非虚拟接口惯用法)。这是一个更通用的被称为 Template Method(一个模式,很不幸,与 C++ templates(模板)无关)的 design pattern(设计模式)的特殊形式。我将那个 non-virtual function(非虚拟函数)(例如,healthValue)称为 virtual function's wrapper(虚拟函数的外壳)。

NVI idiom(惯用法)的一个优势通过 "do 'before' stuff" 和 "do 'after' stuff" 两个注释在代码中标示出来。这些注释标出的代码片断在做真正的工作的 virtual function(虚拟函数)之前或之后调用。这就意味着那个 wrapper(外壳)可以确保在 virtual function(虚拟函数)被调用前,特定的背景环境被设置,而在调用结束之后,这些背景环境被清理。例如,"before" stuff 可以包括锁闭一个 mutex(互斥体),生成一条日志条目,校验类变量和函数的 preconditions(前提条件)是否被满足,等等。"after" stuff 可以包括解锁一个 mutex(互斥体),校验函数的 postconditions(结束条件),类不变量的恢复,等等。如果你让客户直接调用 virtual functions(虚拟函数),确实没有好的方法能够做到这些。

涉及 derived classes(派生类)重定义 private virtual functions(私有虚拟函数)(这些重定义函数它们不能调用!)的 NVI idiom 可能会搅乱你的头脑。这里没有设计上的矛盾。重定义一个 virtual function(虚拟函数)指定如何做某些事。调用一个 virtual function(虚拟函数)指定什么时候去做。互相之间没有关系。NVI idiom 允许 derived classes(派生类)重定义一个 virtual function(虚拟函数),这样就给了它们控制功能如何实现的能力,但是 base class(基类)保留了决定函数何时被调用的权利。乍一看很奇怪,但是 C++ 规定 derived classes(派生类)可以重定义 private inherited virtual functions(私有的通过继承得到的函数)是非常明智的。

在 NVI idiom 之下,virtual functions(虚拟函数)成为 private(私有的)并不是绝对必需的。在一些 class hierarchies(类继承体系)中,一个 virtual function(虚拟函数)的 derived class(派生类)实现被期望调用其 base class(基类)的对应物(例如,第 120 页的例子),而为了这样的调用能够合法,虚拟必须成为 protected(保护的),而非 private(私有的)。有时一个 virtual function(虚拟函数)甚至必须是 public(公有的)(例如,polymorphic base classes(多态基类)中的 destructors(析构函数)——参见 Item 7),但这样一来 NVI idiom 就不能被真正应用。

The Strategy Pattern via Function Pointers(经由函数指针实现的策略模式)

NVI idiom 是 public virtual functions(公有虚拟函数)的有趣的可选替代物,但从设计的观点来看,它比装点门也多不了多少东西。毕竟,我们还是在用 virtual functions(虚拟函数)来计算每一个角色的健康值。一个更引人注目的设计主张认为计算一个角色的健康值不依赖于角色的类型——这样的计算根本不需要成为角色的一部分。例如,我们可能需要为每一个角色的 constructor(构造函数)传递一个指向健康值计算函数的指针,而我们可以调用这个函数进行实际的计算:

class GameCharacter; // forward declaration

// function for the default health calculation algorithm

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;

};

这个方法是另一个通用 design pattern(设计模式)—— Strategy 的简单应用,相对于基于 GameCharacter hierarchy(继承体系)中的 virtual functions(虚拟函数)的方法,它提供了某些更引人注目的机动性:

相同角色类型的不同实例可以有不同的健康值计算函数。例如:class EvilBadGuy: public GameCharacter {

public:

explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)

: GameCharacter(hcf)

{ ... }

...

};

int loseHealthQuickly(const GameCharacter&); // health calculation

int loseHealthSlowly(const GameCharacter&); // funcs with different

// behavior

EvilBadGuy ebg1(loseHealthQuickly); // same-type charac-

EvilBadGuy ebg2(loseHealthSlowly); // ters with different

// health-related

// behavior

对于一个指定的角色健康值的计算函数可以在运行时改变。例如,GameCharacter 可以提供一个 member function(成员函数)setHealthCalculator,它被允许代替当前的健康值计算函数。在另一方面,健康值计算函数不再是 GameCharacter hierarchy(继承体系)的一个 member function(成员函数)的事实,意味着它不再拥有访问它所计算的那个对象内部构件的特权。例如,defaultHealthCalc 不能访问 EvilBadGuy 的 non-public(非公有)构件。如果一个角色的健康值计算能够完全基于通过角色的 public interface(公有接口)可以得到的信息,这就没什么问题,但是,如果准确的健康值计算需要 non-public(非公有)信息,就会有问题。实际上,在任何一个你要用 class(类)外部的等价机能(例如,经由一个 non-member non-friend function(非成员非友元函数)或经由另一个 class(类)的 non-friend member function(非友元成员函数))代替 class(类)内部的机能(例如,经由一个 member function(成员函数))的时候,它都是一个潜在的问题。这个问题将持续影响本 Item 的剩余部分,因为所有我们要考虑的其它设计选择都包括 GameCharacter hierarchy(继承体系)的外部函数的使用。

作为一个通用规则,解决对“non-member functions(非成员函数)对类的 non-public(非公有)构件的访问的需要”的唯一方法就是削弱类的 encapsulation(封装性)。例如,class(类)可以将 non-member functions(非成员函数)声明为 friends(友元),或者,它可以提供对“在其它情况下它更希望保持隐藏的本身的实现部分”的 public accessor functions(公有访问者函数)。使用一个 function pointer(函数指针)代替一个 virtual function(虚拟函数)的优势(例如,具有逐对象健康值计算函数的能力和在运行时改变这样的函数的能力)是否能抵消可能的降低 GameCharacter 的 encapsulation(封装性)的需要是你必须在设计时就做出决定的重要部分。

(未完待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有