码迷,mamicode.com
首页 > 编程语言 > 详细

C++重载操作符的本质

时间:2015-03-30 07:01:08      阅读:175      评论:0      收藏:0      [点我收藏+]

标签:c++重载操作符

重载操作符的本质

重载操作符,在C++中,占有很重要的位置,其本质,也是一般的函数,我们今天就来探讨重载操作符,看看他的真实面目。

 

重载操作符的目的,是为数据结构提供自己的操作方法,例如两个同类型的类相加,其规则到底是什么样的,就在重载操作符中定义。

 

我们先看一段简单的代码:

 

#include<iostream>
using namespace std;
 
class A
{
public:
   int x;
   int y;
   A(int a=0,int b=0):x(a),y(b){};
   A __stdcall operator +(A a)
   {
      A tmp;
      tmp.x=x+a.x;
      tmp.y=y+a.y;
      returntmp;
   }
};
 
int main()
{
   A a(1,1),b(2,2),c;
 
   c=a+b;
 
   cout<<c.x<<endl;
   cin.get();
   return 0;
}


这段代码,没有任何特殊的地方,关键是重载函数A __stdcall operator+(A a)C++中,他的原型到底是什么样的呢?

 

下面的代码,是c=a+b;反汇编的结果:

  


//c=a+b;
01201402  mov        eax,dword ptr [ebp-18h]  
01201405  push       eax  
01201406  mov        ecx,dword ptr [b]  
01201409  push       ecx  
0120140A  lea        edx,[ebp-0FCh]  
01201410  push       edx  
01201411  lea        eax,[a]  
01201414  push       eax  
01201415  call       A::operator+ (120108Ch)  
0120141A  mov        ecx,dword ptr [eax]  
0120141C  mov        edx,dword ptr [eax+4]  
0120141F  mov        dword ptr [c],ecx  
01201422  mov        dword ptr [ebp-28h],edx

 

 

 

我们可以看到,在调用重载操作符前(call A::operator+ (120108Ch),到底需要做哪些准备:


1)b参数的值,压住栈:

01201402  mov        eax,dword ptr [ebp-18h]  
01201405  push       eax  
01201406  mov        ecx,dword ptr [b]  
01201409  push       ecx


 

2)将一个不知道的变量,压住栈(其实这个变量是一个指针,其类型和函数的返回类型一致)

0120140A  lea        edx,[ebp-0FCh]  
01201410  push       edx


 

3)将参数a的地址压入栈:

01201411  lea        eax,[a]  
01201414  push       eax



4)调用函数

01201415  call       A::operator+ (120108Ch)


 

由此我们基本可以推断,函数的原型大概如下:

          

A __stdcall Add(A* This,A* b,A c)



此时我们可以使用如下方法,来将重载操作函数,作为普通函数进行调用,见代码:

 

 

#include<iostream>
using namespace std;
 
class A
{
public:
   int x;
   int y;
   A(int a=0,int b=0):x(a),y(b){};
   A __stdcall operator +(A a)
   {
      A tmp;
      tmp.x=x+a.x;
      tmp.y=y+a.y;
      returntmp;
   }
   A __stdcallshow(){};
};
 
union U
{
   A (__stdcallA::*opt)(A);
   A (__stdcall*fun)(A*,A*,A);
};
 
 
int main()
{
   A a(1,1),b(2,2),c;
 
   c=a+b;
 
   U u;
   u.opt=&A::operator+;
   u.fun(&a,NULL,b);
 
   cout<<c.x<<endl;
   cin.get();
   return 0;
}


 

此时,如果运行程序,将会出现错误,我们将这两个调用:c=a+bu.fun(&a,NULL,b);的反汇编代码列出来:

   //c=a+b;
00D21402 mov         eax,dword ptr[ebp-18h]  
00D21405 push        eax  
00D21406 mov         ecx,dword ptr [b]  
00D21409 push        ecx  
00D2140A lea         edx,[ebp-118h]  
00D21410 push        edx  
00D21411 lea         eax,[a]  
00D21414 push        eax  
00D21415 call        A::operator+(0D2108Ch)  
00D2141A mov         ecx,dword ptr[eax]  
00D2141C mov         edx,dword ptr[eax+4]  
00D2141F mov         dword ptr [c],ecx  
00D21422 mov         dword ptr[ebp-28h],edx  
 
                            //u.fun(&a,NULL,b);
00D2142C mov         esi,esp  
00D2142E mov         eax,dword ptr[ebp-18h]  
00D21431 push        eax  
00D21432 mov         ecx,dword ptr [b]  
00D21435 push        ecx  
00D21436 push        0  
00D21438 lea         edx,[a]  
00D2143B push        edx  
00D2143C lea         eax,[ebp-108h]  
00D21442 push        eax  
00D21443 call        dword ptr [u]  
00D21446 cmp         esi,esp  
00D21448 call       @ILT+320(__RTC_CheckEsp) (0D21145h)


 

 

我们可以看到,u.fun(&a,NULL,b);函数的调用前,多了下列的汇编语句:

00D2143C lea         eax,[ebp-108h]  
00D21442 push        eax


 

这说明,函数的调用方式不对,我们将fun函数的原型,修改如下:

     

void(__stdcall *fun)(A*,A*,A);


 

整个代码如下:

 

#include<iostream>
using namespace std;
 
class A
{
public:
   int x;
   int y;
   A(int a=0,int b=0):x(a),y(b){};
   A __stdcall operator +(A a)
   {
      A tmp;
      tmp.x=x+a.x;
      tmp.y=y+a.y;
      returntmp;
   }
   A __stdcallshow(){};
};
 
 
 
union U
{
   A (__stdcallA::*opt)(A);
   void (__stdcall *fun)(A*,A*,A);
};
 
 
int main()
{
   A a(1,1),b(2,2),c;
 
   c=a+b;
 
   U u;
   u.opt=&A::operator+;
   u.fun(&a,&c,b);
 
   cout<<c.x<<endl;
   cin.get();
   return 0;
}


 

这个运行结果没有问题!!!!说明我们猜对了!

 

大家观察下,此时,有两句代码做了修改:

 

void (__stdcall*fun)(A*,A*,A);

u.fun(&a,&c,b);

 

这是修改后代,这两个函数调用的汇编代码,调用前的汇编准备指令,完全一致!!!

   //c=a+b;
01221402 mov         eax,dword ptr[ebp-18h]  
01221405 push        eax  
01221406 mov         ecx,dword ptr [b]  
01221409 push        ecx  
0122140A lea         edx,[ebp-108h]  
01221410 push        edx  
01221411 lea         eax,[a]  
01221414 push        eax  
01221415 call        A::operator+(122108Ch)  
0122141A mov         ecx,dword ptr[eax]  
0122141C mov         edx,dword ptr[eax+4]  
0122141F mov         dword ptr [c],ecx  
01221422 mov         dword ptr[ebp-28h],edx  
 
   //u.fun(&a,&c,b);
0122142E mov         eax,dword ptr[ebp-18h]  
01221431 push        eax  
01221432 mov         ecx,dword ptr [b]  
01221435 push        ecx  
01221436 lea         edx,[c]  
01221439 push        edx  
0122143A lea         eax,[a]  
0122143D push        eax  
0122143E call        dword ptr [u]


 

我们还可以看到,c=a+b的反汇编语句,在函数被调用完后,多了个变量c的赋值过程。

01221415 call        A::operator+(122108Ch)  
0122141A mov         ecx,dword ptr[eax]  
0122141C mov         edx,dword ptr[eax+4]  
0122141F mov         dword ptr [c],ecx  
01221422 mov         dword ptr[ebp-28h],edx

 

 

而使用u.fun(&a,&c,b),则直接将函数结果,保存在了变量c中!执行效率更高!

 

这样,我们就可以确定,重载操作符A::operator+的原型是:

      void (__stdcall*fun)(A*,A*,A);

 

且中间的一个变量可以用来接收函数的返回值,而函数本身编程了void类型。

 

总结:

   C++中的重载操作符,本身也是一个普通的函数。注意,本例是在VC中完成的,对于其他编译器,可能会有不同的结果。从这个分析,我们也可以看出,C代码确实比C++代码要高效。

 


本文出自 “C” 博客,请务必保留此出处http://5412097.blog.51cto.com/5402097/1626172

C++重载操作符的本质

标签:c++重载操作符

原文地址:http://5412097.blog.51cto.com/5402097/1626172

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!