中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++中的復制構造函數是怎樣的

發布時間:2021-09-24 09:23:20 來源:億速云 閱讀:160 作者:柒染 欄目:開發技術

這篇文章給大家介紹C++中的復制構造函數是怎樣的,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

普通變量的復制

有時我們會在定義一個變量的同時使用另一個變量來初始化它。

int a_variable=12;
int new_variable(a_variable);

通過已有的同類型變量來初始化自身很有用。
對自定義類型的對象是否可以通過一個存在的對象方便的復制呢?

復制構造函數

復制構造函數又叫做拷貝構造函數,它只有一個參數(既然需要復制,一個就夠了,若傳入兩個相同對象則沒有意義,若傳入兩個不同的對象,就沒必要叫做復制構造函數了),參數類型為本類的引用。

如果程序員沒有編寫復制構造函數,編譯器會自動生成復制構造函數,在復制構造函數中按照成員變量進行逐字節復制(初學可以這樣理解,實際上,個別編譯器并不總是會自動生成復制構造函數,它們可能采用直接將源對象的各個值復制到目標對象對應的成員變量上,后面會介紹這種情況)。

class MyDate
{
	int day_;
	int year_;
public:
	MyDate(int day, int year)
	{
		day_ = day;
		year_ = year;
	}
	MyDate(const MyDate& date)
	{
		day_ = date.day_;
		year_ = date.year_;
		cout << "Date類的復制構造函數執行了!" << endl;
	}
	~MyDate() {}
};
void test()
{
	MyDate date1(12, 2021);
	MyDate date2(date1);
}
int main()
{
	test();
	system("pause");
	return 0;
}

執行為:

C++中的復制構造函數是怎樣的

對于MyDate(const MyDate& date)參數列表中的const,因為復制構造函數參數另一個對象引用,如果不加const修飾,在此復制構造函數中可能會改變原對象的內容,為了安全起見,應加盡加。

如果程序員編寫了復制構造函數,則編譯器就不會生成默認復制構造函數了(所以編寫了復制構造函數之后就盡量在函數體內實現復制操作,不要定義了復制構造函數卻不完全或不實現復制操作)。

另一方面,所有的構造函數(包括復制構造函數)、析構函數都無法從父類繼承,只能自己實現。

構造函數如果只有一個參數且這個參數為本類對象,就會與復制構造函數起沖突。如圖:

C++中的復制構造函數是怎樣的

復制構造函數的三種調用

復制構造函數在以下3種情況下會被調用:

1.當使用一個A類型對象去初始化另一個A類型的對象時(剛創建好的,已創建好的不算),會調用復制構造函數。注意觀察以下代碼:

Date date1(12,2012);//創建一個date1對象
Date date2(date1);//調用復制構造函數
Date date3=date1;//也會調用復制構造 函數
date2=date1;//date2已存在,不會調用復制構造函數,會調用賦值=操作函數

C++中的復制構造函數是怎樣的

可以看到復制構造函數只調用了兩次。

2.我們都知道C++傳參有傳值和傳引用(指針本質上是傳值,傳的是實參的地址)。如果函數參數是一個自定義對象,那么會調用該自定義對象的復制構造函數。

在傳值的時候,編譯器會開辟一個空間(創建了一個臨時對象)存儲實參的值(這個過程會將實參的各個值分配復制給臨時對象),再將該值壓入棧中。

//類聲明略
void TestFunction(MyDate date)
{
	cout << "TestFunction()執行了!" << endl;
}
void test()
{
	MyDate date1(12, 2021);
	TestFunction(date1);
}
//main函數略

結果如下:

C++中的復制構造函數是怎樣的

3.如果函數的返回值是類MyDate的對象,則函數返回時,會調用該對象的復制構造函數。

//類聲明略
MyDate TestFunction2()
{
	MyDate date1(12, 2021);
	cout << &date1 << endl;
	return date1;
}
void test()
{
	cout << &TestFunction2() << endl;
}
//main函數省略

從復制構造函數內的輸出被兩個地址輸出夾住即可看出在哪里調用了復制構造函數。

C++中的復制構造函數是怎樣的

復制構造函數的禁用

如果不希望自定義類型的復制構造函數被調用。

僅僅不編寫復制構造函數是不行的,編譯器可能會自動生成默認的復制構造函數。應該使用private修飾復制構造函數(這時編譯器就不會生成自動復制構造函數),此時不要實現這個復制構造函數,如下:

C++中的復制構造函數是怎樣的

這樣便既禁止了用戶調用此復制構造函數,又禁止了用戶通過其他成員函數或友元函數間接地調用它(如果我們僅僅把復制構造函數聲明為private,聲明并實現了復制構造函數,雖然避免了用戶直接調用,但成員函數和友元函數還是可以調用,只有不實現它才能永絕后患)。

Bjarne Stroustrup認為如果你希望禁止某些操作,就把它定義為一個私有的成員函數即可。

深拷貝與淺拷貝

如果成員變量含有指針類型,默認復制構造函數并不會將指針指向的內存中的值進行賦值,僅僅將指針存儲的值(也就是一個地址)復制了一次(與我們所希望的不一致)。這時兩個指針指向了同一塊內存空間,一旦一種一個指針所屬的對象聲明周期結束,會調用它自己的析構函數回收指針指向的內存空間。這時另一個指針遍指向了一個垃圾值,這個指針也變為了空懸指針。以上就是我們常提到的淺拷貝。

實際開發當中要竭力避免以上清情況的發生(當成員變量含有指針或動態分配內存等情況)。

深拷貝如下:

class MyDate
{
private:
	char* buffer_;
public:
	MyDate(const char *init);//實現略
	MyDate(const MyDate &date)
	{
		if(date.buffer_!=nullptr)
		{
			buffer_=new char[strlen(date.buffer_)+1];
			strcpy(buffer_,date.buffer_);
		}
		else
		{
			buffer=nullptr;
		}
	}
}

復制構造函數先檢查date中的buffer_的字符串大小,然后分配此大小+1的內存給新創建的對象的buffer_(strlen函數不會計算'\0'字符),最后使用strcpy函數將date的buffer_指向的內存中的內容復制到新創建的對象的buffer_所指向的空間(strcpy函數會吧'\0'字符一并復制)。最后實現了兩個指針指向了不同的存儲空間(兩個空間的內容相同)。

C++中的復制構造函數是怎樣的

如果我們要編寫需要字符的成員時,盡量使用string。它會像其他成員變量一樣進行復制,因為string有自己的復制構造函數。

一定會生成默認復制構造函數嗎?

我們前面提到如果程序員沒有定義自己的復制構造函數,編譯器會為我們生成一個默認復制構造函數。

實際上以下4中情況編譯器會為我們生成默認復制構造函數。

1.沒有為類編寫復制構造函數,但該類含有自定義類型或string等類型作為成員變量時。

2.沒有為類編寫復制構造函數,但該類繼承了一個含有復制構造函數的類時,編譯器會生成默認復制構造函數,在該函數中調用父類的復制構造函數。

3.沒有為類編寫復制構造函數,但是該類定義了虛函數或者該類的父類定義了虛函數。,

4.沒有為類編寫復制構造函數,但是該類有虛基類。

關于C++中的復制構造函數是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

汾阳市| 尼勒克县| 体育| 长葛市| 上栗县| 辛集市| 澄迈县| 北流市| 台东县| 象山县| 吴堡县| 巴林右旗| 阳江市| 大英县| 大埔区| 屏东县| 女性| 乌兰察布市| 云梦县| 利津县| 渑池县| 新昌县| 清流县| 玛多县| 从化市| 浮梁县| 曲沃县| 太白县| 乳山市| 两当县| 旌德县| 吉木萨尔县| 九龙县| 卢湾区| 若尔盖县| 延吉市| 简阳市| 保德县| 滨州市| 天全县| 同仁县|