【 聲明:版權所有,歡迎轉載,請勿用於商業用途。 聯系信箱: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;
}
};
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: }
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);}
};
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: }
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
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;
}
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中使用,務必注意返回值