c++对象赋值运算

对于类的成员需要动态申请堆空间的类的对象,大家都知道,我们都最好要overload其赋值函数和拷贝函数。拷贝构造函数是没有任何返回类型的,这点毋庸置疑。 而赋值函数可以返回多种类型,例如以上讲的void,类本身class1,以及类的引用 class &? 问,这几种赋值函数的返回各有什么异同?
     答:1 如果赋值函数返回的是void ,我们知道,其唯一一点需要注意的是,其不支持链式赋值运算,即a=b=c这样是不允许的!
           2 对于返回的是类对象本身,还是类对象的引用,其有着本质的区别!
               第一:如果其返回的是类对象本身。
    A operator =(A& a)
    {
             if(name!=NULL)
                 delete name;
        this->_id=a._id;
        int len=strlen(a.name);
        name=new char[len+1];
         strcpy(name,a.name);
        return *this;
     }
           其过程是这样的:
                        class1 A("herengnag");
                         class1 B;   
                         B=A;
                     看似简单的赋值操作,其所有的过程如下:
                        1 释放对象原来的堆资源
                        2 重新申请堆空间
                        3 拷贝源的值到对象的堆空间的值
                        4 创建临时对象(调用临时对象拷贝构造函数),将临时对象返回
                       5. 临时对象结束,调用临时对象析构函数,释放临时对象堆内存
my god,还真复杂!!
             但是,在这些步骤里面,如果第4步,我们没有overload 拷贝函数,也就是没有进行深拷贝。那么在进行第5步释放临时对象的heap 空间时,将释放掉的是和目标对象同一块的heap空间。这样当目标对象B作用域结束调用析构函数时,就会产生错误!!
            因此,如果赋值运算符返回的是类对象本身,那么一定要overload 类的拷贝函数(进行深拷贝)!
             第二:如果赋值运算符返回的是对象的引用,
    A& operator =(A& a)
    {
             if(name!=NULL)
                 delete name;
        this->_id=a._id;
        int len=strlen(a.name);
        name=new char[len+1];
         strcpy(name,a.name);
        return *this;
     }
         那么其过程如下:
                    1 释放掉原来对象所占有的堆空间
                    1.申请一块新的堆内存
                    2 将源对象的堆内存的值copy给新的堆内存
                    3 返回源对象的引用
                     4 结束。

     因此,如果赋值运算符返回的是对象引用,那么其不会调用类的拷贝构造函数,这是问题的关键所在!!




复制初始化与赋值的关系

赋值操作,与复制操作不管从过程还是使用方法来看都是那么的想象,所以需要在这里做一个较好的比较和关联。


第一方面:
    有关操作符重载的详细信息我们会在后面做更详细的介绍,这里用于说明,操作符重载只是在我们通常所使用的,C++语言所定义的那些符号的前面加上operator标志,然后他们作为一个函数名来进行定义,函数的参数,就是运算符的运算数,函数的返回值,就是运算符的返回值,其实你把,我们通常见到的那些运算符理解成函数就可以了,C++之所以提出运算符,主要是要与数学中的相关操作对应,并且力求使用方便。但是原理上,仍然是函数的方法。也就是说,按照这种定义,你可以给运算符定义任何一种操作类型(当然你自然是希望他们完成那种你认为是相似的操作)

   赋值运算符,没有什么特别,它是一个双目运算符,那么重载的时候自然有两个参数,但是如果咋类里面重载非static函数,那么只能付一个参数,因为对象调用的时候会直接默认第一个参数为this。注意方法的放回值,是赋值语句的放回值,通常就是左边操作符的值。

 虽然原理上,赋值运算符,两个操作数是类型无关的,但是,按照“=”的常用法则,他们应该是一个类型的操作数,虽然我们经常碰到那种“=”两边是不同类型的对象的时候,但是右边的那个对象经常是通过转换构造函数默认转换成对应的类型的。

  合成(组装)赋值运算符,是在用户没有定义如何赋值运算符重载的情况下,系统编译器加入一个 ,他的默认情况就是将右边操作数对象的每个子对象,按照赋值运算符赋值给对应左边的运算数。
   与构造函数的嵌套的方法类似,子对象如果是内建类型那么赋值语句没有问题,子对象如果是类类型,则调用相应的赋值语句,由于按照前面提到的机制,类对象是一定存在赋值符操作的。所以不会出现普通构造函数出现的没有默认构造函数而出现的问题。这一点和复制初始化构造函数类似。

第二方面:

   复制初始化和赋值操作符,在使用上很相似,所不同的只是复制初始化是给刚刚开辟空间的对象的各个子变量赋值,而赋值操作符是给已经有的赋值。
   复制和赋值,你一定要分清他们的区别是很难的,如果两个操作数的类型相同,复制和赋值的功能就是将内部的那个值统一,那么他们唯一的区别就是,赋值可以是不同的类型(但是通常不做么做)
   其实,我们要时刻盯住,这两个操作,调用的类的函数是不同的,虽然当用户不定义的时候(经常),他们的结果没有什么区别,但是当用户定义的时候就要注意了。
  前面提到,根据复制初始化的需要,事实上,通常不需要自行定义的,但是有些特别的情况,仍然需要。同理赋值的情况也是一样,并且那些情况中两者是通用的,这一点又可以表明了,复制和赋值有多么的想象!

最后
   要时刻紧记,初始化操作符“=”,和赋值操作符"=",虽然使用起来很像,并且结果效果也很想,但是一定要注意他们是不同类型的操作。他们的过程和使用场景都不同。事实上,这个操作符也只用在这两个方面。这是C++设计(或者说C语言设计)的好——最好不要让一个东西饰演两种截然不同的角色。


实例:

#include <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 b = a;//b对象还不存在,所以调用拷贝构造函数,进行构造处理。  
    cout<<b.name<<endl<<b.url<<endl;  
    Internet c("美国在线","www.aol.com");  
    b = c;//b对象已经存在,所以系统选择赋值运算符重载函数处理。  
    cout<<b.name<<endl<<b.url<<endl;  
    system("pause");  
} 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值