读书人

用派生类的指针调用基类的函数为什么也

发布时间: 2012-05-13 16:39:43 作者: rapoo

用派生类的指针调用基类的函数为什么也可以实现多态?

C/C++ code
#include<iostream.h>class A{ public:   virtual void f()     {cout<<"A::f()"<<endl;}     void g()     {cout<<"A::g()"<<endl;}     void h()      {         cout<<"A::h()"<<endl;         f(); g();      }};class B: public A{ public:    void f()    {cout<<"B::f()"<<endl;}    void g()    {cout<<"B::g()"<<endl;}};void main(){    B b;    B * p=&b;    p->h();}


结果:
A::h()
B::f()
A::g()

最近在学习C++的虚函数,书上说要用基类的指针或引用才能实现多态。但上述代码,p是子类的,调用派生来的h(),this指针也是指向B类的,this->f()
假如说,只是简单的调用自己的f()(不是通过虚函数)。那么下一句,this->g()也应当是简单的调用自己的g(),输出是B::f()啊。
或者,派生类的指针调用基类的函数,而此函数中又调用虚函数,也可以实现多态(好像就基类的构造函数不可以)。那么就,当调用h()时候,作用域已经变成A::,此函数体中的调用也都是在A::的作用域中,因为有多态,F()调用的是子类中的,而,g()仍在A::中,调用的是A::中的g()。如果这样解释的话应该好些。

于是就很矛盾了,第一次提问,还请大家多多指教,还有没有多少积分,给不了多少,见谅,呵呵 ^^

[解决办法]
正确答案来啦


首先 对象B 有一个虚函数表vftable 保存了 A B继承体系中唯一一个虚函数f,且表中唯一一项也是第一项就是B::f()地址

那么不管用什么指针,只要指向的对象是B的 再调用f函数时就会选择B本身的虚函数表第一项,就是调用B::f 啦


然后 打印A::h() 很明显,B没有重载这个函数,肯定要走入A::h() 查看h方法反汇编如下,
看到下面一行 就知道,再h中嗲用g编译器已经事先写死了 就是调用A的g方法

call A::g (4157FDh)

综上 疑问解决

Assembly code
        f(); g(); 00416E41  mov         eax,dword ptr [this] 00416E44  mov         edx,dword ptr [eax] 00416E46  mov         esi,esp 00416E48  mov         ecx,dword ptr [this] 00416E4B  call        dword ptr [edx] 00416E4D  cmp         esi,esp 00416E4F  call        @ILT+2795(__RTC_CheckEsp) (415AF0h) 00416E54  mov         ecx,dword ptr [this] 00416E57  call        A::g (4157FDh)
[解决办法]
我说说我的一点理解啊。
当这句 p->h(),派生类指针调用基类函数。此时要进行一个this指针的转换。

p->h()相当于h(this)
此时this是B*;但是h()是A的函数,因此要进行一个指针的转换为this(A*);
转换后h()内部调用调用虚函数f(),即this->f(),因此实现了多态。

其中的指针转换与A *pa=new B一样


[解决办法]
探讨
谢谢 Dinelgua ,回答的很好,呵呵
按照你的观点,是不是指针未被转换成A*(见11楼)?

还有,我还没学过汇编,能问一下,
既然B::继承了h(),h()属于B了,为什么编译器“事先写死”的不是B::g()呢
谢谢了

读书人网 >C++

热点推荐