王朝网络
分享
 
 
 

翻译:Effective C++, 3rd Edition, Item 37: 绝不要重定义一个函数的 inherited default parameter value(通过继承得到的缺省参数值)

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

Item 37: 绝不要重定义一个函数的 inherited default parameter value(通过继承得到的缺省参数值)

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

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

我们直接着手简化这个话题。只有两种函数能被你 inherit(继承):virtual(虚拟的)和 non-virtual(非虚拟的)。然而,重定义一个 inherited non-virtual function(通过继承得到的非虚拟函数)永远都是一个错误(参见 Item 36),所以我们可以安全地将我们的讨论限制在你继承了一个 virtual function with a default parameter value(带有一个缺省参数值的虚拟函数)的情形。

在这种情况下,本 Item 的理由就变得非常地直截了当:virtual functions(虚拟函数)是 dynamically bound(动态绑定),而 default parameter values(缺省参数值)是 statically bound(静态绑定)。

那又怎样呢?你说 static(静态)和 dynamic binding(动态绑定)之间的区别早已塞入你负担过重的头脑?(不要忘了,static binding(静态绑定)也以 early binding(前期绑定)闻名,而 dynamic binding(动态绑定)也以 late binding(后期绑定)闻名。)那么,我们就再来回顾一下。

一个 object(对象)的 static type(静态类型)就是你在程序文本中声明给它的 type(类型)。考虑这个 class hierarchy(类继承体系):

// a class for geometric shapes

class Shape {

public:

enum ShapeColor { Red, Green, Blue };

// all shapes must offer a function to draw themselves

virtual void draw(ShapeColor color = Red) const = 0;

...

};

class Rectangle: public Shape {

public:

// notice the different default parameter value — bad!

virtual void draw(ShapeColor color = Green) const;

...

};

class Circle: public Shape {

public:

virtual void draw(ShapeColor color) const;

...

};

直观地看,它看起来就像这个样子:

现在考虑这些 pointers(指针):

Shape *ps; // static type = Shape*

Shape *pc = new Circle; // static type = Shape*

Shape *pr = new Rectangle; // static type = Shape*

在本例中,ps,pc 和 pr 全被声明为 pointer-to-Shape 类型,所以它们全都以此作为它们的 static type(静态类型)。注意这就使得它们真正指向的东西完全没有区别——无论如何,它们的 static type(静态类型)都是 Shape*。

一个 object(对象)的 dynamic type(动态类型)取决于它当前引用的 object(对象)的 type(类型)。也就是说,它的 dynamic type(动态类型)表明它有怎样的行为。在上面的例子中,pc 的 dynamic type(动态类型)是 Circle*,而 pr 的 dynamic type(动态类型)是 Rectangle*。至于 ps,它没有一个实际的 dynamic type(动态类型),因为它(还)不能引用任何 object(对象)。

dynamic types(动态类型),就像它的名字所暗示的,能在程序运行中变化,特别是通过 assignments(赋值):

ps = pc; // ps's dynamic type is

// now Circle*

ps = pr; // ps's dynamic type is

// now Rectangle*

virtual functions(虚拟函数)是 dynamically bound(动态绑定),意味着被调用的特定函数取决于被用来调用它的那个 object(对象)的 dynamic type(动态类型):

pc->draw(Shape::Red); // calls Circle::draw(Shape::Red)

pr->draw(Shape::Red); // calls Rectangle::draw(Shape::Red)

我知道,这全是老生常谈;你的确已经理解了 virtual functions(虚拟函数)。但是,当你考虑 virtual functions with default parameter values(带有缺省参数值的虚拟函数)时,就全乱了套,因为,如上所述,virtual functions(虚拟函数)是 dynamically bound(动态绑定),但 default parameters(缺省参数)是 statically bound(静态绑定)。这就意味着你最终调用了一个定义在 derived class(派生类)中的 virtual function(虚拟函数)却使用了一个来自 base class(基类)的 default parameter value(缺省参数值)。

pr->draw(); // calls Rectangle::draw(Shape::Red)!

在此情况下,pr 的 dynamic type(动态类型)是 Rectangle*,所以正像你所希望的,Rectangle 的 virtual function(虚拟函数)被调用。在 Rectangle::draw 中,default parameter value(缺省参数值)是 Green。然而,因为 pr 的 static type(静态类型)是 Shape*,这个函数调用的 default parameter value(缺省参数值)是从 Shape class 中取得的,而不是 Rectangle class!导致的结果就是一个调用由“奇怪的和几乎完全出乎意料的 Shape 和 Rectangle 两个 classes(类)中的 draw 声明的混合物”所组成。

ps,pc,和 pr 是 pointers(指针)的事实与这个问题并无因果关系,如果它们是 references(引用),问题依然会存在。唯一重要的事情是 draw 是一个 virtual function(虚拟函数),而它的一个 default parameter values(缺省参数值)在一个 derived class(派生类)中被重定义。

为什么 C++ 要坚持按照这种不正常的方式动作?答案是为了运行时效率。如果 default parameter values(缺省参数值)是 dynamically bound(动态绑定),compilers(编译器)就必须提供一种方法在运行时确定 virtual functions(虚拟函数)的 parameters(参数)的 default value(s)(缺省值),这比目前在编译期确定它们的机制更慢而且更复杂。最终的决定偏向了速度和实现的简单这一边,而造成的结果就是你现在可以享受高效运行的乐趣,但是,如果你忘记留心本 Item 的建议,就会陷入困惑。

这样就很彻底而且完美了,但是看看如果你试图遵循本规则为 base(基类)和 derived classes(派生类)的用户提供同样的 default parameter values(缺省参数值)时会发生什么:

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 = Red) const;

...

};

噢,code duplication(代码重复)。code duplication(代码重复)带来 dependencies(依赖关系):如果 Shape 中的 default parameter values(缺省参数值)发生变化,所有重复了它的 derived classes(派生类)必须同时变化。否则它们就陷入重定义一个 inherited default parameter value(通过继承得到的缺省参数值)。怎么办呢?

当你要一个 virtual function(虚拟函数)按照你希望的方式运行有困难的时候,考虑可选的替代设计是很明智的,而且 Item 35 给出了多个 virtual function(虚拟函数)的替代方法。替代方法之一是 non-virtual interface idiom (NVI idiom)(非虚拟接口惯用法):用 base class(基类)中的 public non-virtual function(公有非虚拟函数)调用 derived classes(派生类)可能重定义的 private virtual function(私有虚拟函数)。这里,我们用 non-virtual function(非虚拟函数)指定 default parameter(缺省参数),同时使用 virtual function(虚拟函数)做实际的工作:

class Shape {

public:

enum ShapeColor { Red, Green, Blue };

void draw(ShapeColor color = Red) const // now non-virtual

{

doDraw(color); // calls a virtual

}

...

private:

virtual void doDraw(ShapeColor color) const = 0; // the actual work is

}; // done in this func

class Rectangle: public Shape {

public:

...

private:

virtual void doDraw(ShapeColor color) const; // note lack of a

... // default param val.

};

因为 non-virtual functions(非虚拟函数)绝不应该被 derived classes(派生类) overridden(覆盖)(参见 Item 36),这个设计使得 draw 的 color parameter(参数)的 default value(缺省值)应该永远是 Red 变得明确。

Things to Remember

绝不要重定义一个 inherited default parameter value(通过继承得到的缺省参数值),因为 default parameter value(缺省参数值)是 statically bound(静态绑定),而 virtual functions ——应该是你可以 overriding(覆盖)的仅有的函数——是 dynamically bound(动态绑定)。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝网络 版权所有