子类是怎样调用父类的成员函数的

 1. 子类是怎样调用父类的成员函数的?即this指针是怎样传递的,需要移动位置吗?
   
********************************************************************************************
答:继承分实继承和虚继承两类。虚继承是为了解决棱形继承时祖父类对象不唯一而提出的继承方式。
    实继承:
        子类,父类,祖父类其内存分配为先祖父类,父类(包含祖父类),子类(包含祖父及父类)
    三类都共用一个this 指针,
        所以传递给父类及祖父类的指针与子类是一样的。
--------------------------------------------------------------------------------------------
举例:
#include <iostream>
using namespace std;
class A
{
    public:
        A():i(1){
        }
        void testa(){
            cout <<"test a "<<i<<endl;
        }
        int i;
};
class B : public A
{
public:
    B():j(2){
    }
    void testb(){
        cout <<"test b "<<j<<endl;
    }
    int j;
};
class C :public B
{
public:
    C():k(3){
    }

    void testc(){
        cout <<"test c "<<k<<endl;
    }
    int k;
};
int main()
{
    C c;
    c.testa();
    c.testb();
    c.testc();
    system("pause");
    return 0;
}
内存布局:实继承
0013FF74  01 00 00 00  .... 祖父类
0013FF78  02 00 00 00  .... 父类
0013FF7C  03 00 00 00  .... 子类
--------------------------------------------------------------------------------------------
构造函数构造过程。
是递归调用,而且构造的this 一直都没有改变。基类返回后,派生类再构造自己的变量。
代码就不贴了。
--------------------------------------------------------------------------------------------

类函数call 时的this 传递, 可以看到传递均为子类的this 指针,没有变化
37:       c.testa();
004017F0   lea         ecx,[ebp-0Ch]                    //this 指针
004017F3   call        @ILT+520(A::testa) (0040120d)
38:       c.testb();
004017F8   lea         ecx,[ebp-0Ch]                    //this 指针,没有变化
004017FB   call        @ILT+445(B::testb) (004011c2)
39:       c.testc();
00401800   lea         ecx,[ebp-0Ch]                    //this 指针,没有变化
00401803   call        @ILT+210(C::testc) (004010d7)

********************************************************************************************
        
   虚继承。
   子类,父类,祖父类其内存分配为先子类,再父类,再祖父类。保证基类只有一份。vc6下有一个
   额外指针vbptr. 指向各父类基址相对于子类基址的偏移。
   三类的this 指针已经指向了三处位置。所以当调用父类,祖父类函数时,需要移动this 指针。
********************************************************************************************
   
举例: 将上例继承关系改为虚继承。
1. 内存布局
0013FF6C  48 00 47 00  H.G. -》 08 00 00 00, 0C 00 00 00
0013FF70  03 00 00 00  ....  // 子类
0013FF74  01 00 00 00  ....  // 祖父类
0013FF78  3C 00 47 00  <.G. -》 FC FF FF FF (-4)
0013FF7C  02 00 00 00  ....  // 父类

2. 成员函数调用过程。
37:       c.testa();
004017F2   mov         eax,dword ptr [ebp-14h] // 取到vbptr
004017F5   mov         ecx,dword ptr [eax+4]   // 取到偏移量
004017F8   lea         ecx,[ebp+ecx-14h]       // 调整为A类对象地址
004017FC   call        @ILT+520(A::testa) (0040120d) 调用成员函数
38:       c.testb();
00401801   mov         edx,dword ptr [ebp-14h] //取到vbptr 指针
00401804   mov         eax,dword ptr [edx+8]   // 取到类B对象偏移
00401807   lea         ecx,[ebp+eax-14h]       // 调整为B类对象地址
0040180B   call        @ILT+445(B::testb) (004011c2) 调用成员函数
39:       c.testc();
00401810   lea         ecx,[ebp-14h]    // 子类this指针
00401813   call        @ILT+210(C::testc) (004010d7) 调用成员函数

3. 构造函数的构造过程。
004019B3   mov         eax,dword ptr [ebp-4]            // 构造该对象布局
004019B6   mov         dword ptr [eax],offset C::`vbtable' (00470048)
004019BC   mov         ecx,dword ptr [ebp-4]
004019BF   mov         dword ptr [ecx+0Ch],offset C::`vbtable' (0047003c)
004019C6   mov         ecx,dword ptr [ebp-4]
004019C9   add         ecx,8                                            // 调整指针
004019CC   call        @ILT+390(A::A) (0040118b)    // 调用祖父类构造
004019D1   push        0                                                    // 通知父类不要构建布局
004019D3   mov         ecx,dword ptr [ebp-4]
004019D6   add         ecx,0Ch                                        // 调整指针
004019D9   call        @ILT+380(B::B) (00401181) // 调用父类构造

a.填充2个vbtable 指针。目前,举例中只看到使用第一项。然后调整指针,调用A类构造。
再调整指针,调用B类构造。注意。B类构造先push 0, 何也?
这个0 是告诉一个执行流程,不要再初始化vbptr 及其基类,只初始化自己的变量即可。
A类是最基本类,所以它不需要vbptr ,故不用push 0.

即如此,我们把B类构造也copy 来吧,以便看清B类初始化Vbptr 的流程。
00401A7A   mov         dword ptr [ebp-4],ecx
00401A7D   cmp         dword ptr [ebp+8],0  // 比较push 进来的参数是0 还是 1
00401A81   je          B::B+37h (00401a97)  // 为0,不要初始化本类的vbptr 及其子类
// 以下是B类对象布局的初始化
00401A83   mov         eax,dword ptr [ebp-4] // 取到this 指针
00401A86   mov         dword ptr [eax],offset B::`vbtable' (00470058)  //填充第一项VTptr
00401A8C   mov         ecx,dword ptr [ebp-4]
00401A8F   add         ecx,8                                // 获得A类的this 指针。
00401A92   call        @ILT+390(A::A) (0040118b) //调用构造
00401A97   mov         ecx,dword ptr [ebp-4]   //非B类初始化时,直接跳转到此,完成B数据初始化
00401A9A   mov         dword ptr [ecx+4],2

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值