分享
 
 
 

孔乙己之三----虚函数(上)

王朝other·作者佚名  2006-07-19
窄屏简体版  字體: |||超大  

本文作者:sodme

本文出处:http://blog.csdn.net/sodme

声明: 本文可以不经作者同意, 任意复制, 转载, 但任何对本文的引用都请保留文章开始前三行的作者, 出处以及声明信息. 谢谢.

终于发现以asm之角度观察和研究问题是如此有趣, 以至于开了头便罢不了手, 也罢也罢, 那就索性将快乐进行到底吧. 秉持一贯的孔乙己之风格, 这一次, 我们来看看c++虚函数的庐山真面目. 由于对文章所拓展出来的文字篇幅难有心理预期, 但估计关于虚函数的内容又会比较繁杂, 为此, 笔者暂且命此文为"虚函数(上)".

虚函数, 是C++最重要的特点之一, 它实现了所谓的动态联编, 以"基类+虚函数"的方式, 通过很方便的改变对象指针, 可以在运行期动态决定执行哪一个对象的操作函数. 那么, c++的虚函数到底是如何实现的? 它的声明与调用跟普通的成员函数有哪些不同呢? 这些, 都将是从此篇文章开始将要探究的东西.

注: 为控制首页长度,以后代码将放在googlepages上提供下载和访问, 不再在文后附原码. 本文所用c++及asm代码通过以下url访问或下载, 代码使用gcc 4.2, 在fc 4.0下编译(为顺利理解本文, 请务必结合源码阅读): http://sodme.dev.googlepages.com/kyj_03_code.txt

为了尽可能降低学习的门槛, 我们先从最简单的虚函数实例讲起: 为MyClass类添加一个以virtual关键字声明的虚函数test1,并在main中调用test1, 我们将重点考察这两个问题:

1.在main中, 虚函数test1是如何被调用的;

2.为了调用虚函数test1, 需要事先作哪些准备工作, 这些准备工作又是如何作的.

与上文中的asm代码相比较, 我们发现, 当添加了一个 virtual test1() 之后, 在main中, 为对象申请内存空间时, 传的长度参数是12, 比原来的8字节多了4字节, 那为什么多了这4字节? 它的作用又是什么呢? ( 嘘..., 知道的朋友先别说... )

再将两份asm代码相比较, 我们又发现, MyClass构造函数内的执行逻辑也发生了变化:

A(没有test1()虚函数的版本):

movl 8(%ebp), %eax

movl $1, (%eax)

movl 8(%ebp), %eax

movl $2, 4(%eax)

B(有test1()虚函数的版本):

movl $_ZTV7MyClass+8, %edx

movl 8(%ebp), %eax

movl %edx, (%eax)

movl 8(%ebp), %eax

movl $1, 4(%eax)

movl 8(%ebp), %eax

movl $2, 8(%eax)

构造函数内的语句, 只是两条赋值语句, 分别是给data1和data2赋值, 当类MyClass内没有虚函数test1()时, 类MyClass的构造函数很本份的在作着给这两个数据成员赋值的活, 但是, 有了virtual test1()后, 构造函数却暗地里加了这几条语句:

movl $_ZTV7MyClass+8, %edx

movl 8(%ebp), %eax

movl %edx, (%eax)

其中, 8(%ebp)仍如上文中说到的: 它保存的是对象的this指针, 所以, 这三条语句连起来的含义就是: 把"$_ZTV7MyClass+8"这个值放到对象的开始地址处, 也即this指针标识的地址处. 接着查下去, 我们发现标号"_ZTV7MyClass"处定义了这样的一串东西:

_ZTV7MyClass:

.long 0

.long _ZTI7MyClass

.long _ZN7MyClass5test1Ev

啊哦, "$_ZTV7MyClass+8"这个地址里, 存放的竟然是test1()的函数地址! 哈哈..., 那也就是说, 构造函数构造了半天, 实际上已经帮我们把虚函数test1()的地址放在了this指针指向的开始地址处, 可以想见, 以后对test1()的调用, 实际上也就转化成了从this指针指向的首地址取一个函数地址, 然后通过这个地址再找到test1()的地址, 最后再执行call操作, 那么, 我们来看看下面的asm是不是这样作的呢?

接着看, 在asm中, 我们找到了以下对test1函数调用的代码段:

movl -12(%ebp), %eax ;通过eax取得this指针

movl (%eax), %eax ;通过this指针指向的开始地址, 取得存放test1()地址的地址

movl (%eax), %edx ;通过上面的那个什么什么地址, 取得了真正的虚函数test1()的地址

movl -12(%ebp), %eax ;又通过eax取得this指针

movl %eax, (%esp) ;将this指针放在栈顶以供下面调用虚函数所用

call *%edx ;调用虚函数test1()

等等, 说到这里, 可能读者已经有点晕了( 或者有点醒了?! ), 为了弄清楚虚函数定义和调用这件事, 我们把前前后后的故事串起来再讲一遍:

1.编译器在编译时, 事先会为一个类的虚函数统一找个地方, 把他们的地址统一放在一起(这个就是已被无数懂或不懂的人提到的虚函数表);

2.编译时, 将形如"pMyClass->test1()"这样对虚函数进行调用的地方, 统一改成针对于当前对象(例子中是MyClass的对象)的由this牵出的间接函数调用, 所以, 虚函数的寻址方式必然为寄存器寻址(即 call 寄存器 )的形式, 而不会是形如普通成员函数那样的存储器寻址( 即call _ZN7MyClass5printEv)这样的形式. 通过这一点, 我们甚至可以从汇编出来的汇编代码推算此调用是一个虚函数调用(不过, 略显武断了点).

3.在2中, 之所以可以通过this找到这个虚函数, 是因为在对象的构造函数中, 构造函数会把虚函数表的首址放在this指针指向的开始地址处. 也正是由于this指针开始地址处包含了这个虚函数表指针, pMyClass的对象大小才由原来的8字节变成了12字节. 还是由于这this指针指向的开始地址处放的已经是虚函数表指针而不是对象的数据成员, 所以, 对data1和data2的引用, 分别变成了"this+4"和"this+8".

虽然, 通过这个小小的例子, 我们看到了虚表, 看到了构造函数的中介作用, 看到了虚函数的调用方式. 但是, 这个例子毕竟太简单了点, 它没有涉及单继承, 没有涉及多继承, 没有涉及纯虚函数, 太多太多复杂的东西都没有涉及, 所以, 这样看来, 对虚函数的研究还有点日子呢.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有