zoukankan      html  css  js  c++  java
  • 用汇编的眼光看C++(之虚函数) 四

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


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

    1. class employee  
    2. {  
    3. public:  
    4.     employee() { }  
    5.     ~employee() {}  
    6.     virtual void print() const { printf("employee!\n");}  
    7. };  
    8.   
    9. class manager : public employee  
    10. {  
    11. public:  
    12.     manager() {}  
    13.     ~manager() {}  
    14.     void print() const {printf("manager!\n");}  
    15. };  
        我们看到,和前面出现的成员函数稍微有一些不同,这里的print函数之前出现了virtual。然而正是这个virtual发挥了巨大的作用。可以毫不夸张地说,没有虚函数,基本上就没有设计模式,也就无法体现C++语言在面向对象设计中的巨大优越性。下面我们看看这个virtual是怎样发挥作用的?

    1. 76:       employee p;  
    2. 0040128D   lea         ecx,[ebp-10h]  
    3. 00401290   call        @ILT+45(employee::employee) (00401032)  
    4. 00401295   mov         dword ptr [ebp-4],0  
    5. 77:       manager m;  
    6. 0040129C   lea         ecx,[ebp-14h]  
    7. 0040129F   call        @ILT+65(manager::manager) (00401046)  
    8. 004012A4   mov         byte ptr [ebp-4],1  
    9. 78:       employee* e = &p;  
    10. 004012A8   lea         eax,[ebp-10h]  
    11. 004012AB   mov         dword ptr [ebp-18h],eax  
    12. 79:       e->print();  
    13. 004012AE   mov         ecx,dword ptr [ebp-18h]  
    14. 004012B1   mov         edx,dword ptr [ecx]  
    15. 004012B3   mov         esi,esp  
    16. 004012B5   mov         ecx,dword ptr [ebp-18h]  
    17. 004012B8   call        dword ptr [edx]  
    18. 004012BA   cmp         esi,esp  
    19. 004012BC   call        __chkesp (00408870)  
    20. 80:       e = &m;  
    21. 004012C1   lea         eax,[ebp-14h]  
    22. 004012C4   mov         dword ptr [ebp-18h],eax  
    23. 81:       e->print();  
    24. 004012C7   mov         ecx,dword ptr [ebp-18h]  
    25. 004012CA   mov         edx,dword ptr [ecx]  
    26. 004012CC   mov         esi,esp  
    27. 004012CE   mov         ecx,dword ptr [ebp-18h]  
    28. 004012D1   call        dword ptr [edx]  
    29. 004012D3   cmp         esi,esp  
    30. 004012D5   call        __chkesp (00408870)  
    31. 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函数,以上这段代码会发生什么差别呢?

    1. 76:       employee p;  
    2. 0040127D   lea         ecx,[ebp-10h]  
    3. 00401280   call        @ILT+45(employee::employee) (00401032)  
    4. 00401285   mov         dword ptr [ebp-4],0  
    5. 77:       manager m;  
    6. 0040128C   lea         ecx,[ebp-14h]  
    7. 0040128F   call        @ILT+65(manager::manager) (00401046)  
    8. 00401294   mov         byte ptr [ebp-4],1  
    9. 78:       employee* e = &p;  
    10. 00401298   lea         eax,[ebp-10h]  
    11. 0040129B   mov         dword ptr [ebp-18h],eax  
    12. 79:       e->print();  
    13. 0040129E   mov         ecx,dword ptr [ebp-18h]  
    14. 004012A1   call        @ILT+5(employee::print) (0040100a)  
    15. 80:       e = &m;  
    16. 004012A6   lea         ecx,[ebp-14h]  
    17. 004012A9   mov         dword ptr [ebp-18h],ecx  
    18. 81:       e->print();  
    19. 004012AC   mov         ecx,dword ptr [ebp-18h]  
    20. 004012AF   call        @ILT+5(employee::print) (0040100a)  
    21. 82:   }  
        很遗憾,这里就没有了动态查找的过程,所有的打印函数最终都指向了函数employee::print,此时多态性也不复存在了。

  • 相关阅读:
    HDU 1394Minimum Inversion Number 数状数组 逆序对数量和
    2016中国大学生程序设计竞赛(长春)-重现赛 1010Ugly Problem 回文数 模拟
    Codeforces 723C. Polycarp at the Radio 模拟
    hihoCode 1078 : 线段树的区间修改
    hihocode 1077 : RMQ问题再临-线段树
    POJ 2352Stars 树状数组
    Codeforces 714C. Sonya and Queries Tire树
    Codeforces 710C. Magic Odd Square n阶幻方
    Codeforces 709C 模拟
    ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) D. The Door Problem 2-SAT
  • 原文地址:https://www.cnblogs.com/sier/p/5676487.html
Copyright © 2011-2022 走看看