賦值運算符重載函數

本文轉自:http://zhedahht.blog.163.com/blog/static/25411174200741543224391/


問題:給出如下CMyString的聲明,要求爲該類型添加賦值運算符函數。

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


當面試官要求應聘者定義一個複製運算符函數時,他會關注如下幾點:

·         是否把返回值的類型聲明爲該類型的引用,並在函數結束前返回實例自身(即*this)的引用?只有返回一個引用,纔可以允許連續賦值。否則如果函數的返回值是void,假設有三個CMyString的對象,str1str2str3,在程序中語句str1=str2=str3將不能通過編譯。

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

·         是否記得釋放實例自身已有的內存?如果忘了在分配新內存之前釋放自身已有的空間,將出現內存泄露。

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

下面是完整的代碼:

/***********
CMyString.h
************/
class CMyString
{
public:
	//默認構造函數
	CMyString();
	//帶參數的構造函數
	CMyString(const char *str);
	//拷貝構造函數
	CMyString(const CMyString& str);
	//賦值構造函數
	CMyString& operator= (const CMyString& str);
	~CMyString();
	void Print();
private:
	char *m_pData;
};

/***********
CMyString.cpp
************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include "CMyString.h"

using namespace std;

//初始給字符串對象分配一個空間,存儲'\0'
CMyString::CMyString()
{
	m_pData = new char[1];
	if(NULL == m_pData)
	{
		printf("No more memory\n");
		m_pData = NULL;
	}
	else
		m_pData[0] = '\0';

}

//拷貝形參字符串的內容的內容
CMyString::CMyString(const char *str)
{
	if(NULL == str)
	{
		m_pData = new char[1];
		if(NULL == m_pData)
		{
			printf("No more memory\n");
		}
		else
			m_pData[0] = '\0';
	}
	else
	{
		int len = strlen(str);
		m_pData = new char[len + 1];
		if(NULL == m_pData)
		{
			printf("No more memory\n");
		}
		else
		{
			strcpy(m_pData,str);
			m_pData[len] = '\0';
		}
	}
}

/*複製構造函數,傳入的參數是常引用,這樣就避免了傳參時,從形參到實參會調用一次構造拷貝函數,
因爲不改變參數值得內容,所以聲明爲const引用*/
CMyString::CMyString(const CMyString& str)
{
	if(NULL == str.m_pData)
	{
		m_pData = new char[1];
		if(NULL == m_pData)
		{
			printf("No more memory\n");
		}
		else
			m_pData[0] = '\0';
	}
	else
	{
		int len = strlen(str.m_pData);
		m_pData = new char[len + 1];
		if(NULL == m_pData)
		{
			printf("No more memory\n");
		}
		else
		{
			strcpy(m_pData,str.m_pData);
			m_pData[len] = '\0';
		}
	}
}

//析構函數,釋放空間後,置成員指針變量m_pData爲空
CMyString::~CMyString()
{
	if(NULL != m_pData)
	{
		delete m_pData;
		m_pData = NULL;
	}
}
/*1、返回值的類型聲明爲該類型的引用,並在函數結束前返回實例自身(即*this)的引用,這樣才允許連續賦值
2、傳入的參數的類型聲明爲常量引用,如果傳入的參數不是引用而是實例,那麼從形參到實參會調用一次構造拷貝函數,
把參數申明爲引用可以避免這樣的無謂消耗,能提高代碼的效率,
同時,我們在賦值運算符函數內是不會改變傳入的實例的狀態的,因此應該爲傳入的引用參數加上const關鍵字。
3、記得釋放實例自身已有的內存,避免出現內存泄露
4、判斷傳入的參數是不是和當前的實例(*this)是不是同一個實例?如果是同一個,則不進行賦值操作,直接返回。
如果事先不判斷,就進行賦值,那麼在釋放實例自身的內存的時候就會導致嚴重的問題:當*this和傳入的參數是同一個實例時,
那麼一旦釋放了自身的內存,傳入的參數的內存也同時被釋放了,因此再也找不到需要賦值的內容了。*/
CMyString& CMyString::operator =(const CMyString &str)
{
	if(this != &str)
	{
		int len = strlen(str.m_pData);
		char *pStr = new char[len+1];
		if(NULL == pStr)
		{
			printf("No more memory\n");
		}
		else
		{
			strcpy(pStr,str.m_pData);
			//記得釋放實例自身已有的內存,避免出現內存泄露
			delete m_pData;
			m_pData = pStr;
		}
	}
	return *this;
}

void CMyString::Print()
{
	if(NULL != m_pData)
		printf("%s\n",m_pData);
}

/***********
main.cpp
************/
#include <iostream>
#include <stdio.h>
#include "CMyString.h"

int main()
{
	//char str[] = "Hello World";
	CMyString str1("Hello World5");
	str1 = str1;
	CMyString str2("Good Morning!");
	CMyString str3 = str2 = str1;
	str2.Print();
	return 0;
}

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