劍指offer面試題:賦值運算符重載以及細節上的問題(operator=)

        我們在學習類的時候知道一個完整的類中應該存在大默認函數函數:

       1.構造函數   2.析構函數    3.拷貝構造函數    4.賦值運算符的重載函數  

       5.取地址操作符的重載函數     6.const修飾的取地址操作符的重載函數

       構造函數,析構函數,拷貝構造函數大家應該不怎麼陌生,今天給大家講解一下我理解的賦值運算符重載函數並且講一下里面需要注意的易錯點(這些易錯點都是血與淚的教訓)

首先賦值運算符的重載顧名思義就是:“重新定義一下‘=’這個符號”,可能大家要問什麼時候需要重載運算符?爲什麼要重載呢?

用途:

      首先我們說運算符的重載是針對類生成的對象而言的,當我們有了對象A新對象B,我們想把對象B賦給對象A時(意思就是讓對象A和B完全一樣)這時候就需要用到賦值運算符的重載,在我們類中就有默認的賦值運算符重載函數,但是它適用的場景很少,所以大多數情況我們都需要自己實現賦值運算符的重載。

#include<iostream>
#include<string.h>
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等異常,所以我們就沒有修改原有的對象,也就保障了安全性解決了這個問題

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章