關於賦值運算符函數


來自 劍指offer  名企面試官精講典型編程題


class CMyString
{
	public:
		CMyString(char* pData = NULL);	
		CMyString(const CMyString& str);
		~CMyString(void);
	private:
		char* m_pData;
}

當面試官要求應聘者定義一個賦值運算符函數時,他會在檢查應聘者寫出代碼時關注如下幾點:


1. 是否把返回值的類型聲明爲該類型的引用,並在函數結束前返回實例自身的引用(*this)。只有返回一個引用,纔可以允許連續賦值。否則如果函數的返回值是void,應用該賦值運算符將不能做連續賦值。 str1=str2=str3 就不能通過編譯。

2. 是否把傳入的參數類型聲明爲常量引用。如果傳入的參數不是引用而是實例,那麼從形參到實參會調用一次複製構造函數。把參數聲明爲引用則可以避免這樣的無謂消耗,能提高代碼的效率。同時,我們在賦值運算符函數內不會改變傳入的實例的狀態,因此應該爲傳入的引用參數加上const關鍵字。

3. 是否釋放實例自身已有的內存

4. 是否判斷傳入的參數和當前的實例(*this)是不是同一個實例。如果是同一個,則不進行賦值操作,直接返回。如果事先不判斷就進行賦值,那麼在釋放實例自身的內存的時候就會導致嚴重的問題:當*this和傳入的參數是同一個實例是,那麼一旦釋放了自身的內存,傳入的參數的內存也同時被釋放了,因此再也找不到需要賦值的內容了。


CMyString& CMyString::operator =(const CMyString &str)
{
	if(this == & str)
		return *this;
	delete []m_pData;
	m_pData = NULL;
	m_pData = new char[strlen(str.m_pData) + 1];
	strcpy(m_pData,str.m_pData);
	return *this;
}

考慮異常安全性的解法,高級程序員必備

在前面的函數中,我們在分配內存之前先用delete釋放了實例m_pData的內存。如果此時內存不足導致new char拋出異常,m_pData將是一個空指針,這樣程序非常容易崩潰。這就違背了異常安全性原則。

我們有2中方法解決:

一個簡單的方法是我們先用new分配新內容再用delete釋放已有的內容。這樣只在分配內容成功之後再釋放原來的內容,也就是當分配內存失敗時我們能確保CMyString的實例不會修改。

另一個更好的解決辦法是先創建一個臨時實例,再交換臨時實例和原來的實例。下面是這種思路的參考代碼:

CMyString& CMyString::operator = (const CMyString &str)
{
	if(this != &str)
	{
		CMyString strTemp(str);
		
		char* pTemp = strTemp.m_pData;
		strTemp.m_Data = m_pData;
		m_pData = pTemp;
	}

	return *this;
}

在這個函數中,當程序運行到if的外面時也就是出了該變量的作用域,就會自動調用strTemp的析構函數,把strTemp.m_pData所指向的內存釋放掉。

我們是在CMyString的構造函數裏用new分配內存。如果由於內存不足拋出諸如bad_alloc等異常。我們還沒有改變原來實例的狀態,因此實例的狀態還是有效的。





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