程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 用匯編的眼光看C++(之算術符重載)

用匯編的眼光看C++(之算術符重載)

編輯:C++入門知識

 

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved