C++ String類的構造函數、拷貝構造函數的實現

構造函數、析構函數與賦值函數是每個類最基本的函數,在一些公司的面試中也會經常問到這方面的問題。每個類只有一個析構函數和一個賦值函數,但可以有多個構造函數(包含一個拷貝構造函數,其它的稱爲普通構造函數)。對於任意一個類A,如果不手動編寫上述函數,C++編譯器將自動爲類A生成四個缺省的函數

   A(void);                    // 缺省的無參數構造函數

   A(const A &a);                // 缺省的拷貝構造函數

   ~A(void);                    // 缺省的析構函數

    A& operate =(const A &a);    // 缺省的賦值函數

雖然有自動生成,但是還是有必要手動寫上述函數的。因爲:

(1)如果使用“缺省的無參數構造函數”和“缺省的析構函數”,等於放棄了自主“初始化”和“清除”的機會,C++發明人Stroustrup的好心好意白費了。

(2)“缺省的拷貝構造函數”和“缺省的賦值函數”均採用“位拷貝”而非“值拷貝”的方式來實現,倘若類中含有指針變量,這兩個函數註定將出錯。

下面以類String的設計與實現爲例,深入探討這個道理。String的結構如下:

class String{
private:
	char *m_data;//成員變量,用於保存字符串

public:
	String(const char *str=NULL);//普通構造函數
	String(const String &other);//拷貝構造函數
	~String();//析構函數
	String &operator=(const String &other);//賦值函數
};

String類的普通構造函數和析構函數實現如下:

//String的普通構造函數
String::String(const char *str)
{
	if (str==NULL)
	{
		m_data=new char[1];
		*m_data='\0';
	}
	else
	{
		int length=strlen(str);
		m_data=new char[length+1];
		strcpy(m_data,str);
	}
}
//String類的析構函數
String::~String(void)
{
	delete [] m_data;
}

剛剛上面說,如果不主動編寫拷貝構造函數和賦值函數,編譯器將以“位拷貝”的方式自動生成缺省的函數。倘若類中含有指針變量,那麼這兩個缺省的函數就隱含了錯誤。以類String的兩個對象a,b爲例,假設a.m_data的內容爲“hello”,b.m_data的內容爲“world”。位拷貝拷貝的是地址,而值拷貝則拷貝的是內容。現將a賦給b,缺省賦值函數的“位拷貝”意味着執行b.m_data = a.m_data,雖然b.m_data所指向的內容會變成”hello”,但是這將造成三個錯誤:一是b.m_data原有的內存沒被釋放,造成內存泄露;二是b.m_data和a.m_data指向同一塊內存,a或b任何一方變動都會影響另一方;三是在對象被析構時,m_data被釋放了兩次。

對於編譯器,如果不主動編寫拷貝函數和賦值函數,它會以“位拷貝”的方式自動生成缺省的函數。如果重寫賦值函數和拷貝構造函數後,b.m_data=a.m_data,進行的是值拷貝,會將a.m_data的內容賦給b.m_data,b.m_data還是指向原來的內存區域,但是其內容改變。

有下面4個語句:

String a(“hello”);

String b(“world”);

String c = a;    // 調用了拷貝構造函數,最好寫成 c(a);

c = b; // 調用了賦值函數

第3語句的風格較差,宜改寫成String c(a) 以區別於第4語句

 

下面是類String的拷貝構造函數與賦值函數

// 拷貝構造函數
String::String(const String &other)
{
	// 允許操作other的私有成員m_data
	int length=strlen(other.m_data);
	m_data=new char[length+1];
	strcpy(m_data,other.m_data);
}

//賦值函數
String & String::operator = (const String &other)
{
	//檢查自賦值
	if (this==&other)
	{
		return *this;
	}
	//釋放原有的內存資源
	delete []m_data;
	//分配新的內存資源,並複製內容
	int length=strlen(other.m_data);
	m_data=new char[length+1];
	strcpy(m_data,other.m_data);
	//返回本對象的引用
	return *this;
}

類String拷貝構造函數與普通構造函數(參見9.4節)的區別是:在函數入口處無需與NULL進行比較,這是因爲“引用”不可能是NULL,而“指針”可以爲NULL。

類String的賦值函數比構造函數複雜得多,分四步實現:

(1)      第一步,檢查自賦值。

(2)      第二步,用delete釋放原有的內存資源。如果現在不釋放,以後就沒機會了,將造成內存泄露。

(3)      第三步,分配新的內存資源,並複製字符串。

(4)      第四步,返回本對象的引用,目的是爲了實現象 a = b = c 這樣的鏈式表達。注意不要將 return *this 錯寫成 return this.


Ref

高質量C++/C編程指南

http://man.chinaunix.net/develop/c&c++/c/c.htm#_Toc520634051



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