用汇编的眼光看C++(之算术符重载陷阱)23

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

 

    在算术符重载里面,“=”重载可能是最经常使用的一种。但是好多人就误以为在函数中,凡是类出现“=”的地方,那就是调用算术符重载,其实不然。为什么呢?我们可以看看下面的代码。首先,我们定义一个基本类:

  1. class data  
  2. {  
  3.     char* value;  
  4.     int number;  
  5. public:  
  6.     explicit data(int num = 0){  
  7.         if(num){  
  8.             number = num;  
  9.             value = (char*)malloc(num);  
  10.         }  
  11.     }  
  12.   
  13.     data(const data& d){  
  14.         number = d.get_number();  
  15.         value = (char*)malloc(d.get_number());  
  16.         memmove(value, d.get_point(), d.get_number());  
  17.     }  
  18.   
  19.     ~data(){  
  20.         if(number)  
  21.             free(value);  
  22.     }  
  23.   
  24.     data& operator=(const data& d){  
  25.         if(number)  
  26.             free(value);  
  27.         number = d.get_number();  
  28.         value = (char*)malloc(d.get_number());  
  29.         memmove(value, d.get_point(), d.get_number());  
  30.         return *this;  
  31.       
  32.     }  
  33.   
  34.     int get_number() const {return number;}  
  35.     char* get_point() const {return value;}  
  36. };  

    定义好了函数之后,我们就开始对这个类进行调用,同样代码如下所示:

  1. 45:       data m(10);  
  2. 0040108D   push        0Ah  
  3. 0040108F   lea         ecx,[ebp-14h]  
  4. 00401092   call        @ILT+30(data::data) (00401023)  
  5. 00401097   mov         dword ptr [ebp-4],0  
  6. 46:       data p = m;  
  7. 0040109E   lea         eax,[ebp-14h]  
  8. 004010A1   push        eax  
  9. 004010A2   lea         ecx,[ebp-1Ch]  
  10. 004010A5   call        @ILT+35(data::data) (00401028)  
  11. 004010AA   mov         byte ptr [ebp-4],1  
  12. 47:       p = m;  
  13. 004010AE   lea         ecx,[ebp-14h]  
  14. 004010B1   push        ecx  
  15. 004010B2   lea         ecx,[ebp-1Ch]  
  16. 004010B5   call        @ILT+5(data::operator=) (0040100a)  
  17. 48:   }  

    上面共有三句话,我们逐一进行分析:

    45句:定义了一个临时变量,调用data的构造函数

    46句:出现了一个临时变量p,这里发现data类并没有调用算术符重载函数,而是调用了data的构造函数,根据45句所示,调用的肯定不是普通的构造函数,那么剩下的结果只能是拷贝构造函数

    47句: 和46句的代码是一致的,但是此时调用的函数才是算术符重载函数

    所以说,出现“=”的地方未必调用的都是算术符重载函数,也有可能是拷贝构造函数。那么什么时候是拷贝构造函数,什么时候是算术符重载函数呢?判断的标准 其实很简单。如果临时变量是第一次出现,那么调用的只能是拷贝构造函数,反之如果变量已经存在,就像47句一样,那么调用的只能是算术符重载函数,但是我 们这里定义的算数符重载函数有一个陷阱,不知道大家看出来没有?

    我提示大家一下,这里的算术符重载需不需要判断拷贝的是不是自己呢?

  1. void process()  
  2. {  
  3.     data m(10);  
  4.     data p = m;  
  5.     p = p;  
  6. }  

    这里最后一句,如果算术符可以自己拷贝给自己,代码正常编译和运行都没有问题,但是在某些情况下会出现很多意想不到的情况。大家可以跟着我的思路来:

  1. data& operator=(const data& d){  
  2.     if(this == &d)       /* check whether it is self-copy action */  
  3.         return *this;  
  4.   
  5.     if(number)  
  6.         free(value);  
  7.     number = d.get_number();  
  8.     value = (char*)malloc(d.get_number());  
  9.     memmove(value, d.get_point(), d.get_number());  
  10.     return *this;  
  11.   
  12. }  

    如果上面的代码没有判断复制的对象是不是自己,那么我们发现实际上value的数据实际上已经free掉了。那么此时重新分配内存,拷贝的数据只有天知道是什么数据。原来value指向的内存空间就存在了很大的不确定性,这就是算术符重载的陷阱。

 

【后记: 自此用汇编看C++系列全部结束,下面我们将开始数据结构和算法的讨论,欢迎关注】

posted @ 2012-01-18 11:16  董雨  阅读(158)  评论(0编辑  收藏  举报