从汇编层面深度剖析C++虚函数

发表于:2017-1-04 10:27

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:海枫    来源:51Testing软件测试网采编

分享:
  4. 实现运行时多态的最关键一步
  在造构函数里面设置好的vtable的值,显然,同一类型所有对象内的vtable值都是一样的,并且永远不会改变。下面是main函数生成的汇编代码,它展示了C++如何利用vtable来实现运行时多态。
.globl main
.type   main, @function
main:
.LFB3:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl   %ebp
.cfi_def_cfa_offset 8
movl    %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
andl    $-16, %esp
subl    $32, %esp
leal    24(%esp), %eax
movl    %eax, (%esp)
call    _ZN6DeriveC1Ev
leal    24(%esp), %eax
movl    %eax, 28(%esp)
movl    28(%esp), %eax
movl    (%eax), %eax
movl    (%eax), %edx
movl    28(%esp), %eax
movl    %eax, (%esp)
call    *%edx
movl    $0, %eax
leave
ret
.cfi_endproc
andl    $-16, %esp
subl    $32, %esp
  这两句是为局部变量d和bp在堆栈上分配空间,也即如下的语句:
  Derive d;
  Base *pb;
  leal    24(%esp), %eax
  movl    %eax, (%esp)
  call    _ZN6DeriveC1Ev
  esp+24是变量d的首地址,先将它压到堆栈上,然后调用d的构造函数,相应翻译成C语言则如下:
  Derive::Dervice(&d);
  leal    24(%esp), %eax
  movl    %eax, 28(%esp)
  这里其实是将&d的值赋给pb,也即:
  pb = &d;
  最关键的代码是下面这一段:
  movl    28(%esp), %eax
  movl    (%eax), %eax
  movl    (%eax), %edx
  movl    28(%esp), %eax
  movl    %eax, (%esp)
  call    *%edx
  翻译成C语言也就传神的那句:
  pb->vtable[0](bp);
  编译器会记住f虚函数放在vtable的第0项,这是编译时信息。
  5. 小结
  这里省略了很多关于编译器和C++的细枝未节,是出于讨论方便用的需要。从上面的编译代码可以看到以下信息:
  1.每个类都有各有的vtable结构,编译会正确填写它们的虚函数表
  2. 对象在构造函数时,设置vtable值为该类的虚函数表
  3.在指针或者引用时调用虚函数,是通过object->vtable加上虚函数的offset来实现的。
  当然这仅仅是g++的实现方式,它和VC++的略有不同,但原理是一样的。
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号