分享
 
 
 

Guru of the Week 条款05:覆写虚拟函数

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

GotW #05 Overriding Virtual Functions

著者:Herb Sutter

翻译:kingofark

[声明]:本文内容取自www.gotw.ca网站上的Guru of the Week栏目,其著作权归原著者本人所有。译者kingofark在未经原著者本人同意的情况下翻译本文。本翻译内容仅供自学和参考用,请所有阅读过本文的人不要擅自转载、传播本翻译内容;下载本翻译内容的人请在阅读浏览后,立即删除其备份。译者kingofark对违反上述两条原则的人不负任何责任。特此声明。

Revision 1.0

Guru of the Week 条款05:覆写虚拟函数

难度:6 / 10

(虚拟函数(virtual function)真是一个招人喜欢的基本特性,对吗?好吧,如果你能回答下面这个问题,你就会发现她们有时真是冷若冰霜、寒气刺骨。)

[问题]

当你在一个布满灰尘的角落翻寻公司的存档代码时,你偶然中发现了无名氏编写的一段如下的程序。这位无名氏程序员好像曾经试图用这个程序做试验,看看某些C++特性的运作状况。你知道这个程序员希望程序运行后打印什么结果吗?你知道程序运行后的实际结果吗?

#include <iostream>

#include <complex>

using namespace std;

class Base {

public:

virtual void f( int ) {

cout << "Base::f(int)" << endl;

}

virtual void f( double ) {

cout << "Base::f(double)" << endl;

}

virtual void g( int i = 10 ) {

cout << i << endl;

}

};

class Derived: public Base {

public:

void f( complex<double> ) {

cout << "Derived::f(complex)" << endl;

}

void g( int i = 20 ) {

cout << "Derived::g() " << i << endl;

}

};

void main() {

Base b;

Derived d;

Base* pb = new Derived;

b.f(1.0);

d.f(1.0);

pb->f(1.0);

b.g();

d.g();

pb->g();

delete pb;

}

[解答]

首先,我们谈谈编码风格方面的问题:

1. void main()

这并不是一个合法的main声明,尽管许多编译器都允许这种写法。应该使用“int main()”或者“int main(int argc, char* argv[])”。

然而,其实你仍然不需要添加任何返回语句(虽然有时加上返回语句是为了形成一种向外部调用者报告错误的良好编码风格)。实际上,如果main没有返回语句,其效果等同于执行了“return 0;”。

2. delete pb;

这看起来好像既无公害又无伤大雅——当然,前提是Base的编写者提供了一个虚拟析构函数(virtual destructor)。然而,就像我们在这个程序中所看到的那样,在没有虚拟析构函数(virtual destructor)的情况下通过指向基类的指针进行删除操作,这简直就是邪恶的犯罪,同时也是幼稚和简单的——如此以来,崩溃就是你所能期待的最好的事情了。

[规则]:把基类的析构函数(virtual destructor)声明为virtual。

3. Derived::f(complex<double>)

Derived并没有重载(overload)Base::f,而是隐藏了它。这个细节非常重要,因为这意味着在Derived中,Base::f(int)和Base::f(double)是不可见的!(更何况某些流行的编译器对此种情况甚至都不给出一个警告信息。)

[规则]:当派生类中的函数与基类中的函数同名,而你又不想在派生类中隐藏这些基类函数的时候,请使用using声明来把它们定置到可用范围(scope)之内。

4. Derived::g(int i = 10)

除非你是故意要把别人搞糊涂,否则不要改变你所覆写(override)的继承函数(inherited function)的缺省参数。(一般来说,进行覆写而不使用参数缺省值并不是一个坏主意,但那是其本身的问题。)是的,这是一个合法合理的C++语句;不错,其结果也被很好的定义了;但是,不,请不要这样做。往下接着看,你会发现它到底是如何把人搞糊涂的。

[规则]:绝不要改变覆写的继承函数(overridden inherited function)

好了,让我们不要再谈那些编码风格的琐事了,现在我们就来看看主程序到底是不是按照那位无名氏程序员所期望的方式运作的。

void main() {

Base b;

Derived d;

Base* pb = new Derived;

* b.f(1.0);

没问题,调用Base::f(double)。

* d.f(1.0);

这条语句调用Derived::f(complex<double>)。为什么?如前所述,Derived中没有声明“using Base::f;”,所以Base::f(int)和Base::f(double)都不能被调用。

无名氏先生也许原本是想要调用Base::f(double),但是此处他连一个编译错误信息也得不到,因为幸运的是(?),complex<double>包含有一个从double的隐式转换(*),因而编译器把它看成是Derived::f(complex<double>(1.0))。

(*)注:在现有的C++标准草案中,转换构造函数(conversion constructor)不是显式的(explicit)。

* pb->f(1.0);

有趣的事情在这里发生了:虽然Base* pb指向一个Derived对象,但这个语句还是调用Base::f(double),因为重载解析过程(overload resolution)会以静态类型(在这里是指Base)完成,而不是以动态类型(在这里是指Derived)完成。

* b.g();

这条语句只是简单的打印“10”,因为它引起对Base::g(int)的调用,其函数参数缺省值为10。这没什么好奇怪的。

* d.g();

这条语句打印“Derived::g()20”,因为它引起对Derived::g(int)的调用,其函数参数缺省值为20。这也没什么好奇怪的。

* pb->g();

这条语句打印“Derived::g()10”。这个结果也许会暂时死锁你的精神刹车,导致你声嘶力竭的进入一种精神停滞状态——直到你认识到,编译器的所作所为是再正常不过的了(虽然我们可怜的无名氏毫无疑问的应该被枪毙)。要记住,确定缺省参数和重载一样,是以对象的静态类型(在这里是指Base)完成的,因此选用了缺省值10。然而,函数又恰好是virtual的,所以实际被调用的函数取决于对象的动态类型(在这里是指Derived)。

最后,如果你能理解这剩下的几个语句(尽管你会因此说:“噢呕!”),那么你就终于可以理解我开头所说的“冷若冰霜、寒气刺骨”的意思了。祝贺你!

* delete pb;

}

删除操作,当然,会留下一些只被部分清除的东西,使内存变得不可捉摸……好好看看前面关于虚拟析构函数(virtual destructor)的叙述吧。

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