=====hjiaming说:
so easy!!!很有趣哦!
你的程序好像有错误哦!
girl *the_pretty_home
= &pretty;
boy *the_handsome_home = &pretty;-->???????
下面这个pretty应该是handsome吧!
结果:
Preety, I love you, marry me!
I love you too...But I am too
shy to say...
Pretty, you are too sexy, I want to kiss you...
You do
what you want..
程序本身构建两个类,分别是Boy和Girl,每个类中各有两个成员函数,LoveYou(float)和KissYou(float)。主函数先构造两个对象,pretty和handsome,声明*the_pretty_home
和*the_handsome_home 两个指针变量分别指向两个对象。接下来就是调用相应的成员函数实现程序的输出了!
不过,stuman,我请教一个问题,我不知道成员函数中的参数具体有什么作用啊,能否告知!!
=====stuman说:
几点申明:
1、程序没有错误的,否则无法编译成功,也就不能运行和打印出结果了。
2、程序是用标准的C++写的。
3、关于hjianming朋友提出的问题(的确非常仔细!普通人非常容易忽略这个问题):
girl
*the_pretty_home = &pretty;
boy *the_handsome_home = &pretty;-->???????
下面这个pretty应该是handsome吧!
这个pretty是pretty没错(不是我写错了),不是handsome!
另外参数的秘密嘛~~~~~~不能说,你再想想:)
=====leoq说:
哈哈,函数重载,调用基类函数,有点意思
Preety,
I love you, marry me!
Preety, I love you, marry me!
You do what you
want...
You do what you want...
这是结果?
=====hjiaming说:
我发现不管是pretty还是handsome,运行结果是一样的,但我搞不明白为什么会这样啊,哎,C++功底不够啊!stuman 快告诉我啊
=====amakusa说:
Preety, I love you, marry me!"
Preety, I love you, marry
me!"
Pretty, you are too sexy, I want to kiss you..."
You do what you
want..."
=====amakusa说:
555 答案是错的啊 唉 还需努力啊
最后一个输出 我搞不清是call
继承来的成员函数 还是自身的
c++ 不好 见笑了 从学习到工作 一直都是object pascal
=====stuman说:
我把Object Pascal代码写出来。(略)
=====amakusa说:
Delphi中
成员函数被重载了
the_pretty_home.LoveYou(50); //这里也必须是整型参数了
c++中
the_pretty_home
->LoveYou(52.1f);
c++编译器不会报告参数类型错误吗
=====stuman说:
the_pretty_home
->LoveYou(52.1f);
c++编译器是不会报告参数类型错误的,它用隐含的强制类型转换,如果设置上严格一点,编译器也大不了给个警告出来,编译完全能通过的。所以题目也就利用了这点来迷惑人,嘻嘻~~
不过Delphi就不同了,它对参数类型的要求十分严格,我只好用
the_pretty_home.LoveYou(52);
使用整数参数了(泄露天机!),55555
现在我就公布答案了:)
这只是一个概念问题吧。girl的成员函数对boy的成员函数非常类似于重载(reload),但在概念上叫做“隐藏”(hide)。具体说,girl的LoveYou隐藏了boy的LoveYou,girl的KissYou隐藏了boy的KissYou,所以,对girl的派生类来说,boy的两个成员函数都是存在的。至于说参数为什么不一样,只是为了演示参数类型对“隐藏”的影响和与“重载”的重要区别:
girl和boy两个类中两个LoveYou()函数可能用“近似重载"来勉强解释,但是两个KissYou(float result)函数的形式一模一样,能用“重载”来解释吗?重载的函数必须参数不同的,对吧?重载也不可能发生在两个类中。
最让人迷惑的就是为什么the_pretty_home和the_handsome_home被赋予同样的地址(&pretty),按道理说the_pretty_home.LoveYou和the_handsome_home.LoveYou(还有两个KissYou)的结果应该完全一样,但竟然结果不同!那么更深层次的理解需要用类的内存构象来解释,涉及到编译器的处理,我想我们没必要探讨那么深入了。
这里,我们只要明白在隐藏的情况下,boy.LoveYou是能通过派生类girl调出来的。这在实践上可能没什么用处,不过在程序调试上万一出现这种错误,发现结果不对,百思不解,那么要当心这类问题了!
用调侃的解释来说:男孩娶了女孩以后,如果女孩管教不严,那么男孩可能会隐藏一些私房钱(好危险哦,小心他花心哦),这些私房钱是他自己的,但是没有上交给女孩,所以适当的时候他还是能花掉这些钱的。要彻底拿掉这些私房钱,女孩必须用强力手段“overload”(覆盖)来镇压!
正确的输出结果是:
Preety, I love you, marry me!
I love you too...But
I am too shy to say...
Pretty, you are too sexy, I want to kiss you...
You do what you want..
我祝福天下有情人终成眷属!
=====liuxing说:
对于C++来说,最强的功能是指针,最容易犯错的也是指针。本例中,关键是二个类的结构是完全相同的。这时候C++的编译器会根据最适合的方法来解释:
girl *the_pretty_home = &pretty;//女孩说:我不要离开家……不嫁给你(真的?)!
boy
*the_handsome_home = &pretty;//男孩说:来我家吧,我爱你!
换句话说,这时,编译器并不知道the_handsome_home,the_pretty_home
到底是boy还是girl。必须根据具体的方法调用时才能确定。
。上例的结果实际上,严格来说并不准确,只能说对VC是正确的,对不同的编译器会有不同的处理方法,其结果也不同。
=====hjiaming说:
我试过了,在C++Builder的编译器中,结果是一样的。
======stuman说:
我们需要更深层次来讨论这个问题,我反汇编了相关代码,这里是main()段:
:00401060 55
push ebp
:00401061
8BEC mov
ebp, esp
:00401063 83EC10
sub esp, 00000010
:00401066 8D45FC
lea eax, dword ptr [ebp-04]
;[ebp-04]为&pretty
:00401069 8945F8
mov dword ptr [ebp-08], eax
;[ebp-08]为[the_pretty_home]
;girl *the_pretty_home = &pretty;
:0040106C 8D4DFC
lea ecx, dword ptr [ebp-04]
:0040106F 894DF0
mov dword ptr [ebp-10], ecx
;[ebp-10]为[the_handsome_home]
;boy
*the_handsome_home = &pretty;
:00401072 6866665042
push 42506666
:00401077 8B4DF0
mov ecx, dword ptr [ebp-10]
:0040107A E890FFFFFF call 0040100F
;the_handsome_home->LoveYou(52.1f);
:0040107F 6A34
push 00000034
:00401081
8B4DF8 mov ecx,
dword ptr [ebp-08]
:00401084 E890FFFFFF
call 00401019
;the_pretty_home ->LoveYou(52.1f);
:00401089 6866665042 push
42506666
:0040108E 8B4DF0
mov ecx, dword ptr [ebp-10]
:00401091 E86FFFFFFF
call 00401005
;the_handsome_home->KissYou(52.1f);
:00401096 6866665042
push 42506666
:0040109B 8B4DF8
mov ecx, dword ptr [ebp-08]
:0040109E E87BFFFFFF
call 0040101E
;the_pretty_home ->KissYou(52.1f);
:004010A3 8BE5
mov esp, ebp
:004010A5 5D
pop ebp
:004010A6 C3
ret
可以看到,the_pretty_home和the_handsome_home指针的数值的确是相同的,都指向了&pretty
但是为什么
the_handsome_home->LoveYou(52.1f);和
the_pretty_home->LoveYou(52.1f);
却得到了不同的结果。汇编代码提示出,在调用成员函数的时候已经被编译成不同的地址:一个是call 0040100F,一个是call 00401019。
经过函数跳转表后,call 0040100F是下面的代码:
* Referenced by a (U)nconditional or (C)onditional
Jump at Address:
|:0040100F(U)
|
:004010C0 55
push ebp
:004010C1
8BEC mov
ebp, esp
:004010C3 51
push ecx
:004010C4 894DFC
mov dword ptr [ebp-04], ecx
:004010C7
6814104000 push 00401014
* Possible StringData Ref from Data Obj ->"Preety, I love you, marry me!"
|
:004010CC 6850BD4100
push 0041BD50
:004010D1 B9E0F44100
mov ecx, 0041F4E0
:004010D6 E875030000
call 00401450
:004010DB 8BC8
mov ecx, eax
:004010DD E81EFFFFFF call 00401000
:004010E2 8BE5
mov esp, ebp
:004010E4 5D
pop ebp
:004010E5 C20400
ret 0004
非常明显,这就是boy类的成员函数LoveYou();而call
00401019同样可以证明是girl类的成员函数LoveYou();
最后我的结论是:上面的隐藏规则在编译的时候就确定和实现了,也即是“静态绑定”。C++类的指针不仅和它的值有关系,还和它的类别有关系。也就是说,编译器一定要知道the_handsome_home,the_pretty_home
到底是boy还是girl,才能确定具体的方法调用。这和C具有重大的区别。