happia
GotW #5: 覆盖虚函数
原文link:
http://www.gotw.ca/gotw/005.htm
难度:6/10
虚函数太easy了?是吗?如果你能答出这道题,就算你牛。
问题:
你在公司代码库里乱翻,发现一段这样的代码,不知道谁写的,看起来像是在试验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()
虽然很多编译器都允许,但这是错的。用int main() 或者int main(int argc, char * argv[])。不过,并不需要显式的返回语句。当然返回错误代码是好的,不过即使没返回,编译器也会自动添上return 0的。
2.delete pb;
看起来没错,但只是看起来,除非你在基类定义了虚析构函数。delete一个指向基类的指针,如果没有虚析构函数,你就等着掉坑里吧。
[规则]:基类的析构函数定义为虚函数。而且要有函数体,即使是纯虚析构函数。
3.Derived::f(complex<double>)
Derived类不会重载基类的f(),而是隐藏它。这个差别很重要,因为这样基类的f(int)和f(double)在Derived类里是不可见的。而且要小心,很多编译器对这种情况是不报警的。
[规则]:在派生类里写了一个与基类函数名一样的函数时,如果你不想隐藏基类的那个函数,就用using语句把它包含进来。
4.Derived::g(int i = 10)
除非你确实想把别人搞晕,否则不要修改继承函数的默认参数。尽管这符合语法规定,而且也有良好的定义,但别这么干。至于后果请往下看。
[规则]:覆盖继承得来的函数时,永远不要修改默认参数值。
看这段代码会是什么结果。
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”,所以Derived类不能调用Base::f(int)和Base::f(double)。可能本意是想调用Base::f(double),但是由于complex<double>有到double的隐式转换,结果编译器也不会报错。结果就是 Derived::f(complex<double>(1.0))。
pb
->
f(
1.0
);
尽管Base* pb指向了Derived类对象,但是这里调用的是Base::f(double)。因为重载是按静态类型(Base)判断的,而不是动态类型(Derived)。
b.g();
这句的输出是10。调用Base::g(int i = 10),没猫腻,很简单。
d.g();
这句的输出是"Derived::g() 20",也很简单的调用Derived::g(int i = 20)。
pb
->
g();
这句的输出是"Derived::g() 10"。搞不懂了吧。。你得搞清楚编译器对这句是怎么处理的。对于重载,默认参数是按静态类型(Base)取的,所以这里的默认参数就是10。但是,该函数是虚的,所以就要按照对象的动态类型(Derived)来调用。
当然,这个码农该拖出去tjjtds。
如果你把这几段话搞懂了,那就真的是懂了。恭喜!
delete pb;
}
这个delete,会破坏内存,部分内容被清理了。参照上面说过的虚析构函数。
posted on 2012-02-22 11:44
高兴
阅读(234)
评论(0)
编辑
收藏
引用
所属分类:
GotW
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
相关文章:
GotW #9: 内存管理 上部
GotW #8:竞态:异常安全
GotW #7:编译时依赖
GotW #6: const的正确用法
GotW #5: 覆盖虚函数
GotW #4 类技术
Gotw#3 使用标准库,或者重访问临时对象
GotW #2 临时对象
GotW #1 变量初始化
网站导航:
博客园
IT新闻
BlogJava
知识库
博问
管理
导航
C++博客
首页
新随笔
联系
聚合
管理
<
2012年2月
>
日
一
二
三
四
五
六
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
统计
随笔 - 10
文章 - 0
评论 - 0
引用 - 0
常用链接
我的随笔
我的评论
我参与的随笔
留言簿
给我留言
查看公开留言
查看私人留言
随笔分类
GotW(9)
(rss)
随笔档案
2012年2月 (10)
搜索
最新评论
阅读排行榜
1. 旧文新译:Herb Sutter纪念Dennis Ritchie(433)
2. GotW #7:编译时依赖(405)
3. GotW #1 变量初始化(296)
4. GotW #9: 内存管理 上部(262)
5. GotW #5: 覆盖虚函数(234)
评论排行榜
1. GotW #1 变量初始化(0)
2. GotW #2 临时对象(0)
3. Gotw#3 使用标准库,或者重访问临时对象(0)
4. GotW #4 类技术(0)
5. GotW #5: 覆盖虚函数(0)