【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】

虚函数是面向对象设计中的一个重要内容。它的出现使得我们只需要相同的接口函数,并可以得到不同的生成结果。但是有些朋友却知其然,不知其所以然,为什么会出现这样的结果,我们可以用一段代码说明问题。首先,我们先定义两个基本类型,一个是employee,一个是manager,看过前面一片博客的朋友应该都有点印象:

class employee
{
public:employee() { }~employee() {}virtual void print() const { printf("employee!\n");}
};class manager : public employee
{
public:manager() {}~manager() {}void print() const {printf("manager!\n");}
};

我们看到,和前面出现的成员函数稍微有一些不同,这里的print函数之前出现了virtual。然而正是这个virtual发挥了巨大的作用。可以毫不夸张地说,没有虚函数,基本上就没有设计模式,也就无法体现C++语言在面向对象设计中的巨大优越性。下面我们看看这个virtual是怎样发挥作用的?

76:       employee p;
0040128D   lea         ecx,[ebp-10h]
00401290   call        @ILT+45(employee::employee) (00401032)
00401295   mov         dword ptr [ebp-4],0
77:       manager m;
0040129C   lea         ecx,[ebp-14h]
0040129F   call        @ILT+65(manager::manager) (00401046)
004012A4   mov         byte ptr [ebp-4],1
78:       employee* e = &p;
004012A8   lea         eax,[ebp-10h]
004012AB   mov         dword ptr [ebp-18h],eax
79:       e->print();
004012AE   mov         ecx,dword ptr [ebp-18h]
004012B1   mov         edx,dword ptr [ecx]
004012B3   mov         esi,esp
004012B5   mov         ecx,dword ptr [ebp-18h]
004012B8   call        dword ptr [edx]
004012BA   cmp         esi,esp
004012BC   call        __chkesp (00408870)
80:       e = &m;
004012C1   lea         eax,[ebp-14h]
004012C4   mov         dword ptr [ebp-18h],eax
81:       e->print();
004012C7   mov         ecx,dword ptr [ebp-18h]
004012CA   mov         edx,dword ptr [ecx]
004012CC   mov         esi,esp
004012CE   mov         ecx,dword ptr [ebp-18h]
004012D1   call        dword ptr [edx]
004012D3   cmp         esi,esp
004012D5   call        __chkesp (00408870)
82:   }

上面是一段函数调用的代码,代码可以稍微有点长。不过没有关系,我们可以按照代码的行数一行一行地去进行说明和理解。

76行: 我们创建了employee类型的一个变量p,这个可以从后面的employee的构造函数可以看出来

 77行: 我们创建了manager类型的一个变量,这个也可以从后面的manager的构造函数看出

78行: 我们创建一个指针临时变量e,它保存了变量p的地址,这一句也比较简单

79行: 我们发现79句下面共有7句汇编,其中第三句、第六句、第七句是平衡堆栈的时候用的,和我们的调用没有关系。那么call的edx是什么东西呢?原来函数调用的顺序是这样的:edx -> [ecx]  ->[ebp-0x18],不知道大家看明白了没有。在内存的第一个字节记录一个指向print函数指针的指针,也就是edx。通过这个edx,我们就可以查找到位于edx地址的内容是什么。后来我们提取出来后发现[edx]的内容正是我们要查找的print函数地址。这里相当于一个二次寻址的过程。

80行: 我们重新对临时变量e进行了赋值,此时e保存的是变量m的地址

81行: 我们发现此时的寻找过程和79行惊奇地一致,原因就在于edx的内容不同罢了。也就是指向函数指针的指针发生了变化而已。

试想一下,如果没有这个virtual函数,以上这段代码会发生什么差别呢?

76:       employee p;
0040127D   lea         ecx,[ebp-10h]
00401280   call        @ILT+45(employee::employee) (00401032)
00401285   mov         dword ptr [ebp-4],0
77:       manager m;
0040128C   lea         ecx,[ebp-14h]
0040128F   call        @ILT+65(manager::manager) (00401046)
00401294   mov         byte ptr [ebp-4],1
78:       employee* e = &p;
00401298   lea         eax,[ebp-10h]
0040129B   mov         dword ptr [ebp-18h],eax
79:       e->print();
0040129E   mov         ecx,dword ptr [ebp-18h]
004012A1   call        @ILT+5(employee::print) (0040100a)
80:       e = &m;
004012A6   lea         ecx,[ebp-14h]
004012A9   mov         dword ptr [ebp-18h],ecx
81:       e->print();
004012AC   mov         ecx,dword ptr [ebp-18h]
004012AF   call        @ILT+5(employee::print) (0040100a)
82:   }

很遗憾,这里就没有了动态查找的过程,所有的打印函数最终都指向了函数employee::print,此时多态性也不复存在了。

【预告: 下一片博客将介绍类中的静态变量和静态函数】
  

用汇编的眼光看C++(之虚函数)相关推荐

  1. 用汇编的眼光看C++(之 总结篇)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 早在八月份的时候,就陆陆续续写了二十多篇用汇编语言看C++的博客内容.在此为了做一个概括,也为 ...

  2. 用汇编的眼光看C++(之退出流程)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 无论是在判断还是在循环的过程中,通常在遇到合适的条件的时候就会退出相应的模块.跳出模块运行的方 ...

  3. 用汇编的眼光看C++ (之x86汇编)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 说到用汇编的眼光看C++语言,那么怎么阅读汇编代码就成了我们需要解决的一个问题.其实,实话说, ...

  4. 用汇编的眼光看C++(之类继承)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 继承是类的一个基本属性,可是在类的继承过程中,函数是怎么初始化?怎么析构的呢?我们不妨看看下面 ...

  5. 用汇编的眼光看C++(之判断流程)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在我们平常的编程当中,用于判断的地方很多,但主要有下面三种方式:if-else:switch: ...

  6. 用汇编的眼光看C++(之拷贝、赋值函数)

    拷贝构造函数和复制函数是类里面比较重要的两个函数.两者有什么区别呢?其实也很简单,我们可以举个例子,加入有这样一个类的定义: [cpp] view plaincopy class apple { pu ...

  7. 用汇编的眼光看C++(之算术符重载陷阱)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在算术符重载里面,"="重载可能是最经常使用的一种.但是好多人就误以为在函 ...

  8. 从汇编的眼光看C++(之指针拷贝)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 指针是编程人员的梦魇,对C语言的开发者是如此,对C++的开发者也是如此.特别是在C++中,如果 ...

  9. 从汇编的眼光看C++(之递归函数与模板类)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 递归,相信有过基本C语言经验的朋友都明白,就是函数自己调用自己.所以,本质上说,它和普通的函数 ...

最新文章

  1. “5G杀手级应用”Cloud VR 华为如何打响5G第一枪
  2. Squid代理服务器安装设置
  3. android cts 编译,使用 Android studio 分析运行 CTS 用例
  4. 贵州师范学院计算机2级报名,贵州省2021年上半年计算机二级报名时间
  5. owls-uddi-matchmaker安装
  6. 你缺钱只有一个原因就是你每天做赚钱的动作太少了
  7. 必须掌握的Java基础知识(一)
  8. Linux /etc/login.defs配置文件
  9. Wacom发布Cintiq Companion 2
  10. word多个文档标签显示在一个窗口
  11. VB实际读写INI文件
  12. 均值(average)与期望(mean)
  13. 哒螨灵使用注意事项_常用杀虫剂-哒螨灵使用方法
  14. 亚马逊最高级别华人科学家任小枫加盟阿里
  15. 游戏行业被黑客攻击勒索怎么办?
  16. __construct 与 __destruct 区别
  17. win32 009 masm32
  18. Android Studio 查看Shar1码
  19. Linux内存管理(二)
  20. php favicon.ico,简单谈谈favicon

热门文章

  1. priority_queue 优先队列
  2. 李大维:互联网人做硬件创业容易产生的七大误解【转载】
  3. virus test
  4. spring-boot-1.4x后@ConfigurationProperties注解舍弃location
  5. 包管理和环境管理软件Anaconda
  6. java基础面试题总结
  7. SharePoint 删除废弃站点步骤
  8. 安卓虚拟机启动后报错: 类似 SDK Manager] Error: Error parsing .....devices.xml 解决方案...
  9. Windows系统创建符号链接文件
  10. elipse手机设备显示Target unknown或者offline解决方法