欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

virtual函数调用的代价

virtual函数调用的代价

定义一个类。
 view plaincopy to clipboardprint?
class CA   
{   
public:   
    void FunA()   
    {   
    };   
};  
class CA
{
public:
  void FunA()
  {
  };
}; 
通过指针调用FunA。
view plaincopy to clipboardprint?
CA* pa = new CA;   
pa->FunA();  
  CA* pa = new CA;
  pa->FunA(); 
此时,转入汇编,我们看看pa->FunA()到底是怎么做的。
pa->FunA();
// 汇编展开如下:
004113E7  mov         ecx,dword ptr [pa] 
// 函数调用。
004113EA  call        CA::FunA (411005h) 
// 看看FunA做了什么。中间做了一个跳转
00411005  jmp         CA::FunA (411420h) 
// 继续展开。
 void FunA()
 {
// EBP压栈,为函数调用保护现场。
00411420  push        ebp  
00411421  mov         ebp,esp 
00411423  sub         esp,0CCh 
00411429  push        ebx  
0041142A  push        esi  
0041142B  push        edi  
0041142C  push        ecx  
//  函数FunA并没有任何操作,为什么汇编展开成这么多,我暂时也不知道为什么。
0041142D  lea         edi,[ebp-0CCh] 
00411433  mov         ecx,33h 
00411438  mov         eax,0CCCCCCCCh 
0041143D  rep stos    dword ptr es:[edi] 
0041143F  pop         ecx  
00411440  mov         dword ptr [ebp-8],ecx 
 };
// 函数返回
00411443  pop         edi  
00411444  pop         esi  
00411445  pop         ebx  
00411446  mov         esp,ebp 
00411448  pop         ebp  
00411449  ret            
至此。函数调用完成。
可是如果我们为FunA声明为virtual,会发生什么呢?
view plaincopy to clipboardprint?
class CA   
{   
public:   
    virtual void FunA()   
    {   
    };   
};  
class CA
{
public:
  virtual void FunA()
  {
  };
};
// 看看汇编吧。
 pa->FunA();
// 调用咱开。
0041146D  mov         eax,dword ptr [pa] 
00411470  mov         edx,dword ptr [eax] 
00411472  mov         esi,esp 
00411474  mov         ecx,dword ptr [pa] 
// 将虚函数的地址,从虚表中取出来,放入到EAX
00411477  mov         eax,dword ptr [edx] 
// 调用虚函数。下面这一句才是真铮的调用FunA。
// 前面的调用均是准备工作,
// 目的是将FunA在CA对象的虚表指针指向的虚表中的地址取出来。
00411479  call        eax  
// 安全性检查,由于是Debug下运行,如下汇编会在release会优化
0041147B  cmp         esi,esp 
0041147D  call        @ILT+355(__RTC_CheckEsp) (411168h) 
对00411479  call        eax  这一句汇编展开。
CA::FunA:
00411005  jmp         CA::FunA (411510h) 
 virtual void FunA()
 {
00411510  push        ebp  
00411511  mov         ebp,esp 
00411513  sub         esp,0CCh 
00411519  push        ebx  
0041151A  push        esi  
0041151B  push        edi  
0041151C  push        ecx  
0041151D  lea         edi,[ebp-0CCh] 
00411523  mov         ecx,33h 
00411528  mov         eax,0CCCCCCCCh 
0041152D  rep stos    dword ptr es:[edi] 
0041152F  pop         ecx  
00411530  mov         dword ptr [ebp-8],ecx 
 };
00411533  pop         edi  
00411534  pop         esi  
00411535  pop         ebx  
00411536  mov         esp,ebp 
00411538  pop         ebp  
00411539  ret         
后面的进入到函数FunA内部,与非virutual的函数调用汇编一致。
上述汇编均在Debug模式下展开。
可以发现,使用virtual的主要效率损失是在函数调用的准备阶段,
我们需要在对象的虚表指针的某一个位置取出函数实际地址,继而转入函数地址进行函数调用。
在Release模式下,我们看看函数调用的准备阶段到底发生了什么。
不使用virtual:
在本机上FunA的调用直接被优化掉了。断点无法命中。
于是我觉得需要给他加一些有意义的代码,我们只看函数调用准备阶段到底做了什么。
pa->FunA();
// 展开时就是一个call指令,直接指向函数地址。
0040100A  call        dword ptr [__imp__rand (4020A4h)] 
使用virtual:
 pa->FunA();
00401024  mov         edx,dword ptr [eax] 
00401026  mov         ecx,eax 
00401028  mov         eax,dword ptr [edx] 
0040102A  call        eax  
// 对call调用展开,发现该函数就是一个ret指令。因为该函数并没有任何有意义的执行语句。
 virtual void FunA()
 {
 };
00401000  ret              
--- No source file -------------------------------------------------------------
00401001  int         3    
00401002  int         3    
00401003  int         3    
00401004  int         3    
00401005  int         3    
00401006  int         3    
00401007  int         3    
00401008  int         3    
00401009  int         3    
0040100A  int         3    
0040100B  int         3    
0040100C  int         3    
0040100D  int         3    
0040100E  int         3    
0040100F  int         3    
--- e:\workspace\virtualdemo\virtualdemo\virtualdemo.cpp -----------------------
};
结论:
不使用virtual,一个成员函数的调用只需要一条call指令。
如果使用virtual,一个成员函数调用,需要额外的三条mov指令来
呵呵,当我们在享用virtual带来的多态时,
也带来了一些效率上的损失。
同时virtual也会抑制inline以及调用优化。
对于不必要的virtual还是不要为好。
个人意见,欢迎拍砖。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功