1、 软件英才网 软件行业驰名招聘网站有需要请联系我们用汇编的眼光看 C+(之算术符重载) 算术符重载是类的有一个特性,但是每个人使用的方法不一样。用的好,则事半功倍;但是如果不正确的使用,则会后患无穷。(1) 简单算术符介绍那什么是算术符重载呢?我们可以举个例子。一般来说,我们定义两个 int 类型的变量的话,我们就可应对这两个类型进行加、减、乘、除的操作,同时还能比较判断、打印、数组操作、*号操作等等。那么如果我们想自己定义的类也具有这样的属性,那我们应该怎么办呢?当然就要算术符重载了。首先,我们对基本 class 做一个定义:cpp view plaincopy class desk pub
2、lic: int price; desk(int value):price(value) desk() desk return *this; ; 下面,可以用一个范例函数说明一下使用的方法:cpp view plaincopy 74: desk n(5); 0040126D push 5 0040126F lea ecx,ebp-10h 00401272 call ILT+0(desk:desk) (00401005) 00401277 mov dword ptr ebp-4,0 75: desk m(10); 0040127E push 0Ah 00401280 lea ecx,ebp-14
3、h 00401283 call ILT+0(desk:desk) (00401005) 00401288 mov byte ptr ebp-4,1 76: n += m; 0040128C lea eax,ebp-14h 0040128F push eax 00401290 lea ecx,ebp-10h 00401293 call ILT+40(desk:operator+=) (0040102d) 77: 软件英才网 软件行业驰名招聘网站有需要请联系我们大家可以把重点放在76句上面,不过74、75句我们也会稍微介绍一下: 74句: 创建 desk 类型的临时变量 n,调用构造函数75句:
4、创建 desk 类型的临时变量 m,调用构造函数76句: 两个 desk 类型的数据相加,但是在汇编的形式上面,我们发现编译器把这段代码解释成函数调用,也就是我们在上面定义的算术符重载函数。(2)new、free 重载在 C+里面,我们不光可以对普通的算术符进行重载处理,还能对 new、free 进行重载。通过重载 new、free,我们还可以加深对代码的认识,正确认识构造、析构、堆内存分配的原理。首先,我们对 new 和 delete 进行重载定义:cpp view plaincopy class desk public: int price; desk(int value):price(v
5、alue) desk() void* operator new(size_t size) return malloc(size); void operator delete (void* pData) if(NULL != pData) free(pData); ; 那么使用呢?cpp view plaincopy 72: desk* d = new desk(10); 0040127D push 4 0040127F call ILT+65(desk:operator new) (00401046) 00401284 add esp,4 00401287 mov dword ptr ebp-
6、18h,eax 0040128A mov dword ptr ebp-4,0 00401291 cmp dword ptr ebp-18h,0 00401295 je process+56h (004012a6) 00401297 push 0Ah 00401299 mov ecx,dword ptr ebp-18h 0040129C call ILT+5(desk:desk) (0040100a) 004012A1 mov dword ptr ebp-24h,eax 004012A4 jmp process+5Dh (004012ad) 004012A6 mov dword ptr ebp-
7、24h,0 004012AD mov eax,dword ptr ebp-24h 004012B0 mov dword ptr ebp-14h,eax 软件英才网 软件行业驰名招聘网站有需要请联系我们 004012B3 mov dword ptr ebp-4,0FFFFFFFFh 004012BA mov ecx,dword ptr ebp-14h 004012BD mov dword ptr ebp-10h,ecx 73: delete d; 004012C0 mov edx,dword ptr ebp-10h 004012C3 mov dword ptr ebp-20h,edx 00401
8、2C6 mov eax,dword ptr ebp-20h 004012C9 mov dword ptr ebp-1Ch,eax 004012CC cmp dword ptr ebp-1Ch,0 004012D0 je process+91h (004012e1) 004012D2 push 1 004012D4 mov ecx,dword ptr ebp-1Ch 004012D7 call ILT+0(desk:scalar deleting destructor) (00401005) 004012DC mov dword ptr ebp-28h,eax 004012DF jmp proc
9、ess+98h (004012e8) 004012E1 mov dword ptr ebp-28h,0 74: 上面是一段普通的 new、delete 使用代码。但是我们发现,简单的一个语句,在汇编器看来,却需要做这么多的内容,这是为什么呢,我们不妨来自习看一看: 72句:汇编中有两个函数调用,一个是 new 调用,也就是我们重定义的 new 函数,一个是构造函数,最后的几行代码主要是把构造函数返回指针赋值给一些临时变量,可忽略73句:汇编中首先让指针和0进行了判断,然后调用了一个函数,似乎没有调用我们的 delete 函数,我们可以跟进去看一下:cpp view plaincopy desk
10、:scalar deleting destructor: 00401410 push ebp 00401411 mov ebp,esp 00401413 sub esp,44h 00401416 push ebx 00401417 push esi 00401418 push edi 00401419 push ecx 0040141A lea edi,ebp-44h 0040141D mov ecx,11h 00401422 mov eax,0CCCCCCCCh 00401427 rep stos dword ptr edi 00401429 pop ecx 0040142A mov dwo
11、rd ptr ebp-4,ecx 0040142D mov ecx,dword ptr ebp-4 00401430 call ILT+75(desk:desk) (00401050) 软件英才网 软件行业驰名招聘网站有需要请联系我们 00401435 mov eax,dword ptr ebp+8 00401438 and eax,1 0040143B test eax,eax 0040143D je desk:scalar deleting destructor+3Bh (0040144b) 0040143F mov ecx,dword ptr ebp-4 00401442 push ec
12、x 00401443 call ILT+80(desk:operator delete) (00401055) 00401448 add esp,4 0040144B mov eax,dword ptr ebp-4 0040144E pop edi 0040144F pop esi 00401450 pop ebx 00401451 add esp,44h 00401454 cmp ebp,esp 00401456 call _chkesp (00408810) 0040145B mov esp,ebp 0040145D pop ebp 0040145E ret 4 上面的代码便是跟到0x40
13、1005之后遇到的代码,这里有一个跳转,真正函数开始的地方是0x401410。这里我们发现函数实际上还是调用了我们定义的 delete 函数和 desk 的析构函数。只不过析构函数一定要放在 delete 调用之前。所以,这里我们就看到了,c+中new 的真正含义就是先分配内存,然后调用构造函数;而 delete 则是先对变量进行析构处理,然后 free 内存,这就是 new 和 delete 的全部意义。掌握了这个基础,可以帮助我们本地对内存进行很好的管理。 (3)friend 算术符重载和普通算术符重载的区别有一种算术符的重载是这样的:cpp view plaincopy class de
14、sk int price; public: desk(int value):price(value) desk() friend desk operator+ (desk ; desk operator +(desk d.price = d1.price + d2.price; return d; 软件英才网 软件行业驰名招聘网站有需要请联系我们 void process() desk d1(3); desk d2(4); desk d = d1 + d2; return; 感兴趣的同学可以汇编看一下,找一找它和普通的非友元函数有哪些区别。不过上面的代码还是让我们看出了一些端倪: a)友元函数
15、不属于类,因为定义的时候我们发现没有 desk:这样的前缀b)友元算术符重载需要比普通的算术符重载多一个输入参数c)友元函数在进行算术重载定义的时候需要多定义一个临时变量 d,这在函数operator+()可以看出来d)友元算术重载函数会破坏原来类地封装性e)友元函数实际上就是全局函数算术运算符使用的经验总结:(1)算术重载函数是一把双刃剑,务必小心使用(2)内部算术符函数优先使用于非友元函数(3)遇到 = 号重载特别注意一下指针(4)重载的时候函数的内容要和重载的运算符一致,不用重载的是+,实际运算的是相减的内容(5)除非特别需要重载,负责别重载(6)重载的时候多复用已经存在的重载运算符(7)new、delete 除了内存管理和测试,一般不重载,全局 new、delete 严谨重载(8)相关运算符重载要在 stl 中使用,务必注意返回值