足坛十大公平竞赛:从汇编的眼光看C++(之指针拷贝)

来源:百度文库 编辑:中财网 时间:2024/04/20 22:57:04

从汇编的眼光看C++(之指针拷贝)

分类: C/C++ 2011-09-27 22:00 314人阅读 评论(2) 收藏 举报

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】


    指针是编程人员的梦魇,对C语言的开发者是如此,对C++的开发者也是如此。特别是在C++中,如果不注意处理类中的指针,非常容易出问题。如果朋友们不相信可以看看下面的代码:

 

view plaincopy to clipboardprint?
  1. class data  
  2. {  
  3.     int* value;  
  4. public:  
  5.     data(int num){  
  6.         if(num > 0)  
  7.             value = (int*)malloc(sizeof(int)* num);  
  8.     }  
  9.   
  10.     ~data(){  
  11.         if(value)  
  12.             free(value);  
  13.     }  
  14. };  
  15.   
  16. void process()  
  17. {  
  18.     data m(10);  
  19.     data p = m;  
  20. }  
    上面的这段问题有没有什么问题?大家可以自己先用笔在草稿纸上面画一画。然后上机用实际环境验证一下。果不其然,系统提示内存发生了错误。为什么呢?就是因为内存发生了两次释放。我们看以看一下process的汇编代码:

 

 

view plaincopy to clipboardprint?
  1. 21:       data m(10);  
  2. 0040105D   push        0Ah  
  3. 0040105F   lea         ecx,[ebp-10h]  
  4. 00401062   call        @ILT+15(data::data) (00401014)  
  5. 00401067   mov         dword ptr [ebp-4],0  
  6. 22:       data p = m;  
  7. 0040106E   mov         eax,dword ptr [ebp-10h]  
  8. 00401071   mov         dword ptr [ebp-14h],eax  
  9. 23:   }  
  10. 00401074   lea         ecx,[ebp-14h]  
  11. 00401077   call        @ILT+5(data::~data) (0040100a)  
  12. 0040107C   mov         dword ptr [ebp-4],0FFFFFFFFh  
  13. 00401083   lea         ecx,[ebp-10h]  
  14. 00401086   call        @ILT+5(data::~data) (0040100a)  
  15. 0040108B   mov         ecx,dword ptr [ebp-0Ch]  
  16. 0040108E   mov         dword ptr fs:[0],ecx  
  17. 00401095   pop         edi  
  18. 00401096   pop         esi  
  19. 00401097   pop         ebx  
  20. 00401098   add         esp,54h  
  21. 0040109B   cmp         ebp,esp  
  22. 0040109D   call        __chkesp (004015b0)  
  23. 004010A2   mov         esp,ebp  
  24. 004010A4   pop         ebp  
  25. 004010A5   ret  
    21行: data调用构造函数,分配内存给value

 

    22行: 这里我们发现程序进行内存拷贝,那么表示m变量value的数值和p变量中value的数值是一样的

    23行:这里函数即将结束,所以系统调用m和p的析构函数,第一次析构的时候value指向的内存被释放,第二次析构的时候由于p变量value的数值非0,所以也需要释放内存,当然也需要进行析构处理,但是此时内存已经释放了,所以内存进行了二次释放,系统报错。

 

    经过上面的研究,我们发现了问题和原因,那么应该怎么解决呢?既然问题是在拷贝函数这里,那么就要对拷贝函数进行特殊处理。目前就我个人理解,有两个方法供大家选择:

    (1)对拷贝构造函数进行private处理,这样一旦出现了拷贝操作,编译器就会提示出错。

 

view plaincopy to clipboardprint?
  1. class data  
  2. {  
  3.     int* value;  
  4.     data(const data&) ;  
  5. public:  
  6.     data(int num){  
  7.         if(num > 0)  
  8.             value = (int*)malloc(sizeof(int)* num);  
  9.     }  
  10.   
  11.     ~data(){  
  12.         if(value)  
  13.             free(value);  
  14.     }  
  15. };  
    (2)编写拷贝构造函数,进行内存深复制

 

 

view plaincopy to clipboardprint?
  1. class data  
  2. {  
  3.     int* value;  
  4.     int number;  
  5. public:  
  6.     data(int num){  
  7.         if(num > 0)  
  8.             value = (int*)malloc(sizeof(int)* num);  
  9.         number = num;  
  10.     }  
  11.   
  12.     data(const data& d){  
  13.         if(NULL != d.get_ptr())  
  14.             value = (int*) malloc(sizeof(int)* d.get_number());  
  15.         number = d.get_number();  
  16.         memmove(value, d.get_ptr(), sizeof(int)* number);  
  17.     }  
  18.   
  19.     ~data(){  
  20.         if(value)  
  21.             free(value);  
  22.     }  
  23.   
  24.     int* get_ptr() const{ return value;}  
  25.     int get_number() const {return number;}  
  26. };  
    我们看到,经过拷贝构造函数的定义后,原来的process函数解可以正常编译通过,没有问题。
    

 

【预告: 下一篇介绍复制运算符中的陷阱】