C++複製構造函數

複製構造函數、賦值操作符、析構函數總稱爲複製控制

定義:是一種特殊的構造函數,具有單個形參,該形參(常用const修飾)是對該類類型的引用

適用情況:

1)初始化對象 ,根據另一個同類型的對象顯式或隱式初始化一個對象

2)作爲實參傳給函數時,複製這個對象

3)函數返回時,複製對象

4)初始化順序容器中的元素

5)根據元素初始化列表初始化數組元素

分類解說:

1. 對象的定義形式

  C++支持兩種初始化:直接初始化(放在圓括號“()”裏)和複製初始化(複製初始化使用”=“)。當用於類類型對象時,初始化的直接形式與複製形式有所不同:直接初始化直接調用與實參匹配的構造函數,複製初始化調用複製構造函數。複製初始化首先使用指定的構造函數創建一個臨時對象,然後用複製構造函數將那個臨時對象複製到正在創建的那個對象。

	string strBook = "1-234-5678"; // 複製初始化
	string dots(10, '.'); // 直接初始化
	string strcopy = string(); // 複製初始化
	string str;// 直接初始化

只有指定單個實參(第一個)或者顯式創建一個臨時對象(第三個)用於複製時,才能使用複製初始化。

聲明:這裏書寫方式的不同,會導致調用形式也不一樣的,而且我在調試中發現與C++Primer有些觀點不一致,如下

	int nObj = 100;
	HasPtr p1, p2; //默認構造函數
	p1 = HasPtr(&nObj, 10); // 構造函數 和 複製操作符
	p2 = HasPtr(p1);  //複製構造函數 和 賦值操作符函數
	HasPtr p3 = HasPtr(&nObj, 10);	 //只發生了複製構造函數的調用
	HasPtr p4 = HasPtr(p1);  //複製構造函數
先說p3默認默認使用HasPtr提供的構造函數先創建一個臨時對象,然後用這個臨時對象直接初始化p3,這裏調試中發現並沒有調用複製構造函數,這裏也就是說臨時對象就是p1,並沒有進行復制,但是書中則是使用了複製構造函數進行復制的,另外p4則是調用了複製構造函數直接初始化p4了;p1與p2首先使用的就是默認構造函數進行初始化的,然後p1,使用了形參爲2個的構造函數,然後再調用賦值操作符函數,但是p2卻調用了複製構造函數,然後再調用賦值操作符函數,這個與前面說的使用情況是一致的。

2. 形參與返回值

 當形參爲非引用類型是,將複製實參的值;以非引用類型做返回值時,將返回return語句中值的副本。

string MakePlural(size_t size, const string& strWord, const string& strEnding)
{
	return (size == 1)? strWord: (strWord+strEnding); //返回一個臨時對象的副本
} 

這裏形參並沒有複製,因爲這裏使用了const引用,如果是非引用就會複製實參的值。另外在返回值裏面,如果size=1那麼就會返回strWord值的副本,而不是其本身,因爲返回類型不是引用;而size!=1的時候,返回的是一個由strWord+strEnding計算後的臨時對象值的副本。

3. 初始化容器元素

   可以用表示容量的的單個形參來初始化容器,如下:

vector<string> vec(5);

首先會使用string默認構造函數創建一個臨時值來初始化vec,然後使用複製構造函數將臨時值複製到vec的每個元素,但是作爲一般規則通常是先分配一個空容器,然後再一個個的加入容器。

4. 構造函數與數組元素

	sales_item primer_eds[] = {
		string("0-201-16578-6"),
		string("0-201-16578-1"),
		string("0-201-16578-8"),
		sales_item()
	};
第一個~第三個使用sales_tem的形參爲string類型的構造函數初始化爲臨時對象,然後複製到數組相應位置處;最後一個沒有指定實參,使用默認構造函數初始化,然後使用複製構造函數複製到最後一個位置上。

合成複製構造函數:

1)如果我們沒有定義,編譯器默認會合成一個,與合成的默認構造函數不同,即使我們定義了其他構造函數,也會合成複製構造函數。

2)合成複製構造函數執行逐個成員初始化,合成複製構造函數直接複製內置類型成員的值,類類型成員使用該類的複製構造函數進行復制,複製數組時將複製數組中的每一個元素。

3)可以將合成複製構造函數看作其中每個數據成員在構造函數初始化列表中進行初始化。例如sale_item的類有幾個成員:

class sales_item
{
...
private:
	string isbn;
	unsigned units_sold;
	double revenue;
...
};  
可以這樣寫複製構造函數:

sales_item::sales_item(const sales_item &org):
	isbn(org.isbn),
	units_sold(org.units_sold),
	revenue(org.revenue)
{	
}

定義自己的複製構造函數:

對於許多類而言,合成複製構造函數只完成必要的工作,只包含類類型成員或者內置類型(非指針)成員的類,無需顯式地定義複製構造函數,也可以複製。

需要自己定義複製構造函數的:

1)有些類必須加以控制,這樣的類經常有一個數據成員是指針,或者有成員在構造函數中分配其他資源。

2)另一些類在創建新對象時必須做一些特定工作。

注意:

1)形參一般定義爲const的引用

2)因爲用於向函數傳遞對象和從函數返回對象,該構造函數一般不應設置爲explict    

禁止複製:

有些類是不需要複製構造函數的,如果不定義複製構造函數也會默認合成一個,所以爲了防止複製必須顯示地聲明一個複製構造函數爲private。然而類的友元和成員仍可以進行復制,如果想連友元和成員中的複製也禁止,可以只聲明一個複製構造函數但不定義。

大多數類應定義複製構造函數和默認構造函數,因爲不允許複製的類對象只能作爲引用傳遞給函數或者從函數中返回,這裏返回是從形參中返回,不是函數體最後的返回值。

如果定義了複製構造函數,也必須定義默認構造函數。

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