剑指offer面试题:赋值运算符重载以及细节上的问题(operator=)

        我们在学习类的时候知道一个完整的类中应该存在大默认函数函数:

       1.构造函数   2.析构函数    3.拷贝构造函数    4.赋值运算符的重载函数  

       5.取地址操作符的重载函数     6.const修饰的取地址操作符的重载函数

       构造函数,析构函数,拷贝构造函数大家应该不怎么陌生,今天给大家讲解一下我理解的赋值运算符重载函数并且讲一下里面需要注意的易错点(这些易错点都是血与泪的教训)

首先赋值运算符的重载顾名思义就是:“重新定义一下‘=’这个符号”,可能大家要问什么时候需要重载运算符?为什么要重载呢?

用途:

      首先我们说运算符的重载是针对类生成的对象而言的,当我们有了对象A新对象B,我们想把对象B赋给对象A时(意思就是让对象A和B完全一样)这时候就需要用到赋值运算符的重载,在我们类中就有默认的赋值运算符重载函数,但是它适用的场景很少,所以大多数情况我们都需要自己实现赋值运算符的重载。

#include
#include
class CMystring
{
public:
	CMystring(char*pdata=NULL);//构造函数
	CMystring(const CMystring&str);//拷贝构造函数
	~CMystring();//析构函数
	CMystring&operator =(const CMystring&str)//赋值运算符的重载
	{
		if(this!=&str)//如果赋值的对象不等于当前对象
		{

         		delete[]pdata;//因为我们要把新对象赋值给旧对象,所以把旧对象原本的内存释放掉
	        	pdata=NULL;//指针置空
	        	pdata=new char[strlen(str.pdata)+1];//开辟和新对象一样大的内存
		        strcpy(pdata,str.pdata);//把新对象的数据传递给旧对象,深拷贝
                }	
        	return *this;
   
	}
private:
	char*pdata;

};

对于这个赋值运算符的中再函数有四个难点:

1.返回值的类型必须为该类型的引用,并且在结束时返回自身的引用(即*this)。只有返回引用我们才可以实现对象的连续赋值(即如果定义三个对象CMstring a,b,c,实现a=b=c),如果返回值是void则无法实现该功能。

2.传入的参数也必须为对象的引用。因为传入的参数为对象实例,那么在形参到实参的过程中会调用一次拷贝构造函数形成无谓的消耗,引用则会避免这样的问题并且提高代码的效率,同时我们在赋值运算符的重载过程中不会改变实例的状态,所以要在传入的引用参数前面加上const。

3.要释放旧对象已有的内存,否则会导致内存泄漏,因为我们要把新的对象的赋给旧对象,流程就是先把旧对象内存释放,再开辟新的内存,最后把值赋予。

4.要判断传入的对象的引用是不是为当前的对象(即a=a这种况)如果是传入的和已有的是同一个对象,那么只需要返回*this。否则当传入的对象和已有对象是同一个对象时,系统在释放旧对象内存的时候同时也把我们传入新对象内存释放掉,这样赋值的时候就找不到赋值的内容了

上述代码就是我们日常生活中常用的解决赋值运算符重载的方法,可以解决日常中遇到的绝大读诵问题。

 

        但是如果我们想要再深入探讨可能出现的错误时就是:根据我们的思路我们先释放旧的内存,然后开辟新内存存放数据,但是如果当我们在开辟新的内存时遇到系统内存不够时怎么办,要知道这时候我们已经把旧内存释放了,但是新内存还开辟不出来,我们不仅没有完成赋值反而把原来的数据还丢掉了。(按照现在计算机的配置出现内存不够的情况很少,如果大家不想探讨看到这里也就可以结束了)

要想解决这个问题我们有两个办法:

1.我们可以先new,再delete。这样只有等分配内存空间成功时才释放旧的空间,如果分配失败也不会影响原来的对象。

2.我们可以先生成一个临时对象,通过交换临时对象的方法来实现辅助(即要想让a=b,可以先让c=b;然后a=c)

我们就用第二个方法来把代码优化一下

CMystring&operator =(const CMystring&str)
	{
                if(&str!=this)
                {
		     CMystring tmp(str);//用拷贝构造函数生成临时对象
		     char*ptmp=tmp.pdata;//通过临时对象值交换实现赋值
		     tmp.pdata=pdata;
		     pdata=ptmp;
                }
                return *this;
	}

       可以看出我们通过拷贝构造函数生成了临时对象然后通过交换实现了赋值,因为临时对象tmp是一个局部变量,所以当if()结束后就超出了它的作用域,他会自己调用析构函数来释放自己所指向的内存,由于它指向的就是我们之前对象的内存,这样就实现了自己释放内存,因为我们在构造函数肯定是用new开辟内存,如果开辟失败要抛出内存不足比如bad_alloc等异常,所以我们就没有修改原有的对象,也就保障了安全性解决了这个问题

 

 

 

 

 

 

你可能感兴趣的:(剑指offer练习题)