中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > C/C++
虚函数的内存结构(二)
作者:未知 时间:2005-09-13 23:28 出处:Blog.ChinaUnix.net 责编:chinaitpower
              摘要:虚函数的内存结构(二)
虚函数的内存结构(二)

一,Class的布局

 

想要理解虚拟函数的内存布局,需要对class的结构入手,先从struct看起:

struct{

             int x,y;

}

            如上的结构体在内存中如何存放呢?可以借用《inside the c++ program     model》的图形来看该内容:

     int  x;

 

     int y; 

 

                

从上图来看在内存中,x,y其实是被分配到一块连续的内存空间中,这块内 存空间的大小是x,y的大小之和(不同的变量类型的话,可能会产生内存对齐的问题,这里不考虑)。

 

当我们有如下一个class的话:

class point

{

private:

int x,y;

publice:

            void first(){};

}

如下声明: point pt;

那么pt 在内存中的存储方式如何呢?事实上它和struct的格式一样。并没有因为它是类而需要更多的信息(如函数名等信息)。而我刚开始的理解不是这样,因为当时没有考虑明白如下的方式中:

point pt;

pt.first();

 

是如何调用函数的。事实上和在C语言中调用函数没有太多区别。

 

二,关于虚函数

 

 先来看一段简单的程序:

class point

{

public:

      void virtual first();

};

 

void point::first()

{

      return;

}

 

 

 

void main()

{

      point pt;

      pt.first();

}

 

反汇编,摘出它的_main函数:

_main:

  00401060: 55                 push        ebp

  00401061: 8B EC              mov         ebp,esp

  00401063: 83 EC 44           sub         esp,44h

  00401066: 53                 push        ebx

  00401067: 56                 push        esi

  00401068: 57                 push        edi

  00401069: 8D 7D BC           lea         edi,[ebp-44h]

  0040106C: B9 11 00 00 00     mov         ecx,11h

  00401071: B8 CC CC CC CC     mov         eax,0CCCCCCCCh

  00401076: F3 AB              rep stos    dword ptr [edi]

  00401078: 8D 4D FC           lea         ecx,[ebp-4]

  0040107B: E8 85 FF FF FF     call        @ILT+0(??0point@@QAE@XZ)

  00401080: 8D 4D FC           lea         ecx,[ebp-4]

  00401083: E8 87 FF FF FF     call        @ILT+10(?first@point@@UAEXXZ)

  00401088: 5F                 pop         edi

  00401089: 5E                 pop         esi

  0040108A: 5B                 pop         ebx

  0040108B: 83 C4 44           add         esp,44h

  0040108E: 3B EC              cmp         ebp,esp

  00401090: E8 5B 00 00 00     call        __chkesp

  00401095: 8B E5              mov         esp,ebp

  00401097: 5D                 pop         ebp

  00401098: C3                 ret

  00401099: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ

  004010A9: CC CC CC CC CC CC CC    

 

我们可以看到对于first的调用,和调用普通成员函数并没有太多区别,也就是说虚拟函数也和普通函数一样的方式被组织在代码段中,通过函数名直接调用。

 

但我们知道,虚拟函数的最大特点,是通过父函数的指针可以调用子函数的方法来实现多态。是如何实现的呢?通过父指针要确定子类的的类型,由于可能有多个子类,而且是运行时刻识别,这样就需要子类的对象本身包含自身的信息。方法其实就在class对象的存储内容上。

再看一个类:

class  point

{

      Private:

                  Int x,y;

     Public:

        Virtual void first(){};

        Virtual void test(){};

}

当我们声明 point pt;的时候,内存中的存放形式如下:

 

     int  x;

 

     int y;

 

    vptr

 

void first();

 

void two();

 

type info

 

从图上可以看到,在pt的结构体内多了一个指针,该指针指向了该类所有的虚拟函数列表。该列表是各个虚拟函数所对应的函数地址。这样在动态调用的时候,就可以通过对象自身,而不是类来确定调用的函数。

       再看一个例子:

class point

{

public:

      void virtual first();

};

 

void point::first()

{

      return;

}

 

 

 

void main()

{

      point *pt = new point();

     

      pt->first();

}

 

现在改用指针来调用该方法,反汇编该代码,摘取_main函数如下:

 

_main:

  00401060: 55                 push        ebp

  00401061: 8B EC              mov         ebp,esp

  00401063: 6A FF              push        0FFh

  00401065: 68 5B 08 41 00     push        41085Bh

  0040106A: 64 A1 00 00 00 00  mov         eax,fs:[00000000]

  00401070: 50                 push        eax

  00401071: 64 89 25 00 00 00  mov         dword ptr fs:[0],esp

            00

  00401078: 83 EC 50           sub         esp,50h

  0040107B: 53                 push        ebx

  0040107C: 56                 push        esi

  0040107D: 57                 push        edi

  0040107E: 8D 7D A4           lea         edi,[ebp-5Ch]

  00401081: B9 14 00 00 00     mov         ecx,14h

  00401086: B8 CC CC CC CC     mov         eax,0CCCCCCCCh

  0040108B: F3 AB              rep stos    dword ptr [edi]

  0040108D: 6A 04              push        4

  0040108F: E8 6C 01 00 00     call        ??2@YAPAXI@Z

  00401094: 83 C4 04           add         esp,4

  00401097: 89 45 E8           mov         dword ptr [ebp-18h],eax

  0040109A: C7 45 FC 00 00 00  mov         dword ptr [ebp-4],0

            00

  004010A1: 83 7D E8 00        cmp         dword ptr [ebp-18h],0

  004010A5: 74 0D              je          004010B4

  004010A7: 8B 4D E8           mov         ecx,dword ptr [ebp-18h]

  004010AA: E8 56 FF FF FF     call        @ILT+0(??0point@@QAE@XZ)

  004010AF: 89 45 E4           mov         dword ptr [ebp-1Ch],eax

  004010B2: EB 07              jmp         004010BB

  004010B4: C7 45 E4 00 00 00  mov         dword ptr [ebp-1Ch],0

            00

  004010BB: 8B 45 E4           mov         eax,dword ptr [ebp-1Ch]

  004010BE: 89 45 EC           mov         dword ptr [ebp-14h],eax

  004010C1: C7 45 FC FF FF FF  mov         dword ptr [ebp-4],0FFFFFFFFh

            FF

  004010C8: 8B 4D EC           mov         ecx,dword ptr [ebp-14h]

  004010CB: 89 4D F0           mov         dword ptr [ebp-10h],ecx

  004010CE: 8B 55 F0           mov         edx,dword ptr [ebp-10h]

  004010D1: 8B 02              mov         eax,dword ptr [edx]

  004010D3: 8B F4              mov         esi,esp

  004010D5: 8B 4D F0           mov         ecx,dword ptr [ebp-10h]

  004010D8: FF 10              call        dword ptr [eax]

  004010DA: 3B F4              cmp         esi,esp

  004010DC: E8 0F 06 00 00     call        __chkesp

  004010E1: 8B 4D F4           mov         ecx,dword ptr [ebp-0Ch]

  004010E4: 64 89 0D 00 00 00  mov         dword ptr fs:[0],ecx

            00

  004010EB: 5F                 pop         edi

  004010EC: 5E                 pop         esi

  004010ED: 5B                 pop         ebx

  004010EE: 83 C4 5C           add         esp,5Ch

  004010F1: 3B EC              cmp         ebp,esp

  004010F3: E8 F8 05 00 00     call        __chkesp

  004010F8: 8B E5              mov         esp,ebp

  004010FA: 5D                 pop         ebp

  004010FB: C3                 ret

 

这段代码中已经找不到含有first形式的函数调用,新的调用方式是: 004010D8: FF 10              call        dword ptr [eax],通过ptr来调用函数。关于ptr的内容有些繁琐,一些细节我还是不太清楚。这边我所知道的就是通过它可以得到虚函数所存在的地址。

        这样当用父指针指向子类对象的时候,由于子类本身带有了虚函数的信息,通过父类就可以直接调用到子类的虚函数,而不是父类自己的虚函数。当不是虚函数的时候,父类是就只能调用自己的函数了。

 

        写到这儿,发现自己对很多概念还是不清晰,如this指针的用法,多重继承下的虚函数等。这篇文章写不清晰,同时发现自己的语文水平也有限!路漫漫其修远,不知道什么时候求索是个头啊!

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有