【 聲明:版權所有,歡迎轉載,請勿用于商業用途。】
算術符重載是類的有一個特性,但是每個人使用的方法不一樣。用的好,則事半功倍;但是如果不正确的使用,則會後患無窮。
(1) 簡單算術符介紹
那什麼是算術符重載呢?我們可以舉個例子。一般來說,我們定義兩個int類型的變量的話,我們就可應對這兩個類型進行加、減、乘、除的操作,同時還能比 較判斷、列印、數組操作、*号操作等等。那麼如果我們想自己定義的類也具有這樣的屬性,那我們應該怎麼辦呢?當然就要算術符重載了。首先,我們對基本 class做一個定義:
[cpp] view plaincopy
- 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進行重載定義:
- 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算術符重載和普通算術符重載的差別
有一種算術符的重載是這樣的:
- 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屬性的一些内容】