從逆向分析角度看C++拷貝建構函式

來源:互聯網
上載者:User

一段C++代碼:

//: HowMany_2.cpp#include <iostream>using namespace std;class HowMany {  static int objectCount;  public:  HowMany() {    ++objectCount;    print("HowMany()");  }  ~HowMany() {    --objectCount;    print("~HowMany()");  }  HowMany(const HowMany& h) {    ++objectCount;    print("HowMany(const HowMany&)");  }    void print(const char ss[]) {    cout << ss << ": ";    cout << "objectCount = " << objectCount << endl;    return ;  }};int HowMany::objectCount = 0;HowMany f(HowMany x) {  x.print("x argument inside f()");  cout << "Return From f()" << endl;  return x;  // 有傳回值 x}int main() {  {    HowMany h;    cout << "Entering f()" << endl;    HowMany h2 = f(h);  }  return 0;} ///:~

運行結果:

 

Assembly Code:

38:   int main() {39:     {40:       HowMany h;004017FD   lea         ecx,[h]; [h] 為對象 h 的記憶體位址00401800   call        @ILT+685(HowMany::HowMany) (004012b2); 調用建構函式00401805   mov         dword ptr [ebp-4],041:       cout << "Entering f()" << endl;0040180C   push        offset @ILT+200(std::endl) (004010cd)00401811   push        offset string "Entering f()" (0046f090)00401816   push        offset std::cout (0047ce98)0040181B   call        @ILT+660(std::operator<<) (00401299); 題外話,觀察一下進棧順序00401820   add         esp,800401823   mov         ecx,eax00401825   call        @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)42:       HowMany h2 = f(h);0040182A   push        ecx0040182B   mov         ecx,esp; 當前 ESP 所指的棧塊作為臨時對象Temp的記憶體位址0040182D   mov         dword ptr [ebp-18h],esp00401830   lea         eax,[h]00401833   push        eax; 將h的記憶體位址[h]壓入堆棧00401834   call        @ILT+0(HowMany::HowMany) (00401005); 調用拷貝建構函式,把h的內容拷貝到Temp的記憶體中00401839   mov         dword ptr [ebp-1Ch],eax0040183C   lea         ecx,[h2]0040183F   push        ecx; 將h2的記憶體位址[h2]壓入堆棧00401840   call        @ILT+640(f) (00401285); 調用f()函數00401845   add         esp,800401848   mov         dword ptr [ebp-20h],eax43:     }0040184B   lea         ecx,[h2]0040184E   call        @ILT+500(HowMany::~HowMany) (004011f9); 調用解構函式,銷毀h200401853   mov         dword ptr [ebp-4],0FFFFFFFFh0040185A   lea         ecx,[h]0040185D   call        @ILT+500(HowMany::~HowMany) (004011f9); 調用解構函式,銷毀h44:     // getchar();45:     return 0;00401862   xor         eax,eax46:   } ///:~

fun()函數的工作機制:

32:   HowMany f(HowMany x) {33:     x.print("x argument inside f()");004015DB   push        offset string "x argument inside f()" (0046f030)004015E0   lea         ecx,[ebp+0Ch]004015E3   call        @ILT+575(HowMany::print) (00401244)34:     cout << "Return From f()" << endl;004015E8   push        offset @ILT+200(std::endl) (004010cd)004015ED   push        offset string "Return From f()" (0046f01c)004015F2   push        offset std::cout (0047ce98)004015F7   call        @ILT+660(std::operator<<) (00401299)004015FC   add         esp,8004015FF   mov         ecx,eax00401601   call        @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)35:     return x;00401606   lea         eax,[ebp+0Ch]; [ebp+0C]為Temp的記憶體位址00401609   push        eax0040160A   mov         ecx,dword ptr [ebp+8]; [ebp+8]為h2的記憶體位址,ecx指向h2記憶體塊0040160D   call        @ILT+0(HowMany::HowMany) (00401005); 調用拷貝建構函式,將Temp的內容拷貝到h2的記憶體中00401612   mov         ecx,dword ptr [ebp-10h]00401615   or          ecx,100401618   mov         dword ptr [ebp-10h],ecx0040161B   mov         byte ptr [ebp-4],00040161F   lea         ecx,[ebp+0Ch]; ecx儲存Temp的記憶體位址00401622   call        @ILT+500(HowMany::~HowMany) (004011f9); 調用解構函式,銷毀Temp00401627   mov         eax,dword ptr [ebp+8]; eax儲存h2的記憶體位址36:   }

對於運行結果

解析如下:

1. 對象h調用建構函式

2. 輸出字串“Entering f()”

3. 在進入f()函數之前,建立了一個臨時對象Temp,調用拷貝建構函式,將對象h的內容拷貝到Temp中

4. 進入f()函數,在執行“return x”語句後,調用拷貝建構函式,將對象Temp的內容拷貝到h2中(完成了h2 = f(h)的工作)

5. 在f()函數結束前,為Temp調用解構函式,銷毀Temp對象

6. 退出f()函數,在main函數結束前,先銷毀對象h2,最後銷毀對象h

----------------------------------------------------------------------------

逆向分析時執行“HowMany h2 = f(h);”語句過程中棧分布的記錄:

*****

進入f()函數前(建立了一個臨時對象Temp,調用拷貝建構函式,將對象h的內容拷貝到Temp中)



進入f()函數後



*********************************************************

再看<<Thinking in C++>>中的一段代碼,更為清晰的講解了拷貝建構函式的機制:

//: HowMany_2.cpp#include <string>#include <iostream>using namespace std;class HowMany {  string name;  static int objectCount;  public:  HowMany(const string& id = "") {    name = id;    ++objectCount;    print("HowMany()");  }  ~HowMany() {    --objectCount;    print("~HowMany()");  }  HowMany(const HowMany& h) {    name = h.name + "copy";    ++objectCount;    print("HowMany(const HowMany&)");  }    void print(const string& msg = "") {    if (msg.length() != 0) {      cout << msg << endl;    }    cout << '\t' << name << ": " << "objectCount = " << objectCount << endl;        return ;  }};int HowMany::objectCount = 0;HowMany f(HowMany x) {  x.print("x argument inside f()");  cout << "Return From f()" << endl;  return x;}int main() {  {    HowMany h("h");    cout << "Entering f()" << endl;    HowMany h2 = f(h);    cout << "Call f(), no return value" << endl;    f(h);    cout << "After call to f()" << endl;  }  // getchar();  return 0;} ///:~

運行結果:


解釋如下:

1. 建立對象h,並調用建構函式

2. 輸出“Entering f()”字串

3. 進入f()函數前,建立一個臨時對象Temp,並調用拷貝建構函式,將對象h的內容拷貝到Temp中

4. 進入f()函數後,在“return x”時,調用拷貝建構函式,將對象Temp的內容拷貝到h2中,在函數結束前調用解構函式,銷毀對象Temp

5. 輸出字串

6.進入f()函數前,建立一個臨時對象Temp,並調用拷貝建構函式,將對象h的內容拷貝到Temp中

7. 進入f()函數後,建立一個臨時對象x,在“return x”時,調用拷貝建構函式,將對象Temp的內容拷貝到x中,在函數結束前調用解構函式,先銷毀對象Temp,再銷毀對象x

8. 在main函數結束前,調用解構函式,先銷毀對象h2,再銷毀對象h

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.