C++运算符重载赋值运算符

结构体变量直接赋值:如果包含指针,赋值只能实现浅拷贝。被赋值的结构体变量的指针成员与原结构体变量的指针成员指向一个地址。

 

我们已经习惯了为结构体变量中的每个成员赋值,那么我们可以在两个结构体变更之间直接使用“=”号赋值吗? 答案是肯定的,因为编译器支持。例如定义一个表示矩形的结构体:

 typedef struct _Rectangle

            

       int x;       // 左上角x坐标             

       int y;       // 左上角y坐标             

       int dx;     // 矩形宽度             

       int dy;     // 矩形高度      

} Rectangle;            

 定义两个矩形结构体变量并赋值:      

Rectangle Rect1, Rect2;      

Rect1.x = 100;       Rect1.y = 100;       Rect1.dx = 100;       Rect1.dy =100;             

 Rect2 = Rect1;             

上面的赋值在C语言中是支持的,编译器会将Rect2 = Rect1中的值转化成内存拷贝的CPU指令来实现赋值操作。可以想象,对于简单的变量赋值,CPU只需要执行一个MOV指令就可以完成了,因此对于包含多个简单变量的结构体来说,使用多个循环的MOV指令就在情理之中了(在早期16位CPU中,如果对一个32位的int变量执行赋值操作都需要两条MOV指令)。

在使用这种赋值方法的时候需要注意的是,在这个结构体变量中最好不要有指针变量,因为指针变量可能在变量1中指向一个分配的内存区域,当变量2通过赋值操作获得了这个指针值的时候,有可能这个指针已经释放了,这样就导致了空指针情况的发生,后果是使用这个指针的时候将会导致程序崩溃。

举例说明如下:      

 1、定义一个包含指针类型的结构体:

typedef struct _TestStruct

{        int nMember;        int *Ptr; }TestStruct;

2、定义两个这种类型的变量并采用如下使用方法:

TestStruct Struct1, Struct2; Struct1.nMember = 100;

Struct1.Ptr = (int *)malloc(15*sizeof(int)); // 分配15个int变量的空间

// 结构体赋值 Struct2 = Struct1;

//此时Struct2.Ptr与 Struct1.Ptr的值相等

 if(Struct1.Ptr) {        free(Struct1.Ptr);        Strict1.Ptr = NULL; } // 这里有很复杂的处理,其中包含了malloc等操作 Struct2.Ptr[0] = 2;// 错误的赋值操作,因为此时Struct2.Ptr所指向的内容已经被释放了。

对于程序来说,修改一个已经释放了空间的内存地址内容是十分危险的。当然,如果程序只有上面那么简单的话也不会出现什么严重的问题,顶多只是非法使用了一块内存区域;但是,如果中间含有复杂的处理,Struct2.Ptr[0] = 2将修改程序其他部分使用的内存区域,那么这样就可能会有莫名其妙的死机之类的事情发生了。由于其发生问题的时间不固定,因此这类问题调试起来也十分的困难。

 

赋值运算符重载函数将用来解决这一问题:

 

自定义类的赋值运算符重载函数的作用与内置赋值运算符的作用类似,但是要要注意的是,它与拷贝构造函数与析构函数一样,要注意深拷贝浅拷贝的问题,在没有深拷贝浅拷贝的情况下,如果没有指定默认的赋值运算符重载函数,那么系统将会自动提供一个赋值运算符重载函数。 赋值运算符重载函数的定义与其它运算符重载函数的定义是差不多的。 下面我们以实例说明如何使用它,代码如下:

//程序作者:管宁  
//站点:www.cndev-lab.com  
//所有稿件均有版权,如要转载,请务必著名出处和作者  
  
#include <<FONT color=maroon>iostream
     
using namespace std;      
      
class Internet  
 
    public 
        Internet(char *name,char *url)  
         
            Internet::name = new char[strlen(name)+1];  
            Internet::url = new char[strlen(url)+1];  
            if(name)  
             
                strcpy(Internet::name,name);  
             
            if(url)  
             
                strcpy(Internet::url,url);  
             
         
        Internet(Internet &temp)  
         
            Internet::name=new char[strlen(temp.name)+1];  
            Internet::url=new char[strlen(temp.url)+1];  
            if(name)  
             
                strcpy(Internet::name,temp.name);  
             
            if(url)  
             
                strcpy(Internet::url,temp.url);  
             
         
        ~Internet()  
         
            delete[] name;  
            delete[] url;  
         
        Internet& operator =(Internet &temp)//赋值运算符重载函数  
         
            delete[] this->name;  
            delete[] this->url;  
            this->name = new char[strlen(temp.name)+1];  
            this->url = new char[strlen(temp.url)+1];  
            if(this->name)  
             
                strcpy(this->name,temp.name);  
             
            if(this->url)  
             
                strcpy(this->url,temp.url);  
             
            return *this 
         
    public 
        char *name;  
        char *url;  
};  
int main()  
   
    Internet a("中国软件开发实验室","www.cndev-lab.com");  
    Internet = a;//b对象还不存在,所以调用拷贝构造函数,进行构造处理。  
    cout<<b.name<<endl<<b.url<<endl;  
    Internet c("美国在线","www.aol.com");  
    = c;//b对象已经存在,所以系统选择赋值运算符重载函数处理。  
    cout<<b.name<<endl<<b.url<<endl;  
    system("pause");  
}

上例代码中的Internet& operator =(Internet &temp)就是赋值运算符重载函数的定义,内部需要先delete的指针就是涉及深拷贝问题的地方,由于b对象已经构造过,name和url指针的范围已经确定,所以在复制新内容进去之前必须把堆区清除,区域的过大和过小都不好,所以跟在后面重新分配堆区大小,而后进行复制工作。 

在类对象还未存在的情况下,赋值过程是通过拷贝构造函数进行构造处理(代码中的Internet b = a;就是这种情况),但当对象已经存在,那么赋值过程就是通过赋值运算符重载函数处理(例子中的b = c;就属于此种情况)。


版权声明:本文为u013203733原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。