【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
算术符重载是类的有一个特性,但是每个人使用的方法不一样。用的好,则事半功倍;但是如果不正确的使用,则会后患无穷。
(1) 简单算术符介绍
那什么是算术符重载呢?我们可以举个例子。一般来说,我们定义两个int类型的变量的话,我们就可应对这两个类型进行加、减、乘、除的操作,同时还能比较判断、打印、数组操作、*号操作等等。那么如果我们想自己定义的类也具有这样的属性,那我们应该怎么办呢?当然就要算术符重载了。首先,我们对基本class做一个定义:
-
class
desk
-
{
-
public:
-
int price;
-
-
desk(
int
value):price(
value) {}
-
~desk() {}
-
desk&
operator+= (desk& d){
-
this->price += d.price;
-
return *
this;
-
}
-
};
下面,可以用一个范例函数说明一下使用的方法:
-
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-14h]
-
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句: 创建desk类型的临时变量m,调用构造函数
76句: 两个desk类型的数据相加,但是在汇编的形式上面,我们发现编译器把这段代码解释成函数调用,也就是我们在上面定义的算术符重载函数。
(2)new、free重载
在C++里面,我们不光可以对普通的算术符进行重载处理,还能对new、free进行重载。通过重载new、free,我们还可以加深对代码的认识,正确认识构造、析构、堆内存分配的原理。
首先,我们对new和delete进行重载定义:
-
class desk
-
{
-
public:
-
int price;
-
-
desk(
int value):price(value) {}
-
~desk() {}
-
void* operator new(size_t size) {
return
malloc(size);}
-
void operator delete (void* pData) {
if(
NULL != pData)
free(pData);}
-
};
那么使用呢?
-
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-
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-
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
-
004012C6 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 process+98h (004012e8)
-
004012E1 mov dword ptr [ebp-28h],0
-
74: }
上面是一段普通的new、delete使用代码。但是我们发现,简单的一个语句,在汇编器看来,却需要做这么多的内容,这是为什么呢,我们不妨来自习看一看:
72句:汇编中有两个函数调用,一个是new调用,也就是我们重定义的new函数,一个是构造函数,最后的几行代码主要是把构造函数返回指针赋值给一些临时变量,可忽略
73句:汇编中首先让指针和0进行了判断,然后调用了一个函数,似乎没有调用我们的delete函数,我们可以跟进去看一下:
-
desk::`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 dword 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 ecx
-
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
-
上面的代码便是跟到0x401005之后遇到的代码,这里有一个跳转,真正函数开始的地方是0x401410。这里我们发现函数实际上还是调用了我们定义的delete函数和desk的析构函数。只不过析构函数一定要放在delete调用之前。所以,这里我们就看到了,c++中new的真正含义就是先分配内存,然后调用构造函数;而delete则是先对变量进行析构处理,然后free内存,这就是new和delete的全部意义。掌握了这个基础,可以帮助我们本地对内存进行很好的管理。
(3)friend算术符重载和普通算术符重载的区别
有一种算术符的重载是这样的:
-
class
desk
-
{
-
int price;
-
public:
-
desk(
int
value):price(
value) {}
-
~desk() {}
-
friend desk
operator+ (desk& d1, desk& d2);
-
};
-
-
desk
operator +(desk& d1, desk& d2)
-
{
-
desk d(0);
-
d.price = d1.price + d2.price;
-
return d;
-
}
-
-
void process()
-
{
-
desk d1(3);
-
desk d2(4);
-
desk d = d1 + d2;
-
return;
-
}
感兴趣的同学可以汇编看一下,找一找它和普通的非友元函数有哪些区别。不过上面的代码还是让我们看出了一些端倪:
a)友元函数不属于类,因为定义的时候我们发现没有desk::这样的前缀
b)友元算术符重载需要比普通的算术符重载多一个输入参数
c)友元函数在进行算术重载定义的时候需要多定义一个临时变量d,这在函数operator+()可以看出来
d)友元算术重载函数会破坏原来类地封装性
e)友元函数实际上就是全局函数
算术运算符使用的经验总结:
(1)算术重载函数是一把双刃剑,务必小心使用
(2)内部算术符函数优先使用于非友元函数
(3)遇到 = 号重载特别注意一下指针
(4)重载的时候函数的内容要和重载的运算符一致,不用重载的是+,实际运算的是相减的内容
(5)除非特别需要重载,负责别重载
(6)重载的时候多复用已经存在的重载运算符
(7)new、delete除了内存管理和测试,一般不重载,全局new、delete严谨重载
(8)相关运算符重载要在stl中使用,务必注意返回值
【预报: 下面博客开始介绍const属性的一些内容】