从逆向分析角度看C++拷贝构造函数

来源:互联网 发布:郑州java培训排名 编辑:程序博客网 时间:2024/05/01 00:19

一段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