c++拷貝構造(複製構造函數)詳解||淺拷貝和深拷貝||拷貝構造函數的參數能用值傳遞嗎?

目錄

 

1.拷貝構造函數

2.淺拷貝和深拷貝

3.拷貝構造函數的參數能用值傳遞嗎?


1.拷貝構造函數

首先,簡單介紹一下拷貝構造函數。拷貝構造函數是構造函數的一種,它在創建對象時,是使用同一類中之前創建的對象來初始化新創建的對象。

拷貝構造函數將創建好(已初始化)的對象作爲參數,返回一個新的對象。

如果我們沒有定義拷貝構造函數,系統會自動生成一個默認的拷貝構造函數。

拷貝構造函數的一般形式如下所示:

ClassName(const ClassName& obj)
{}

我們呢來看一個實例:

class  People
{
public:
	//構造函數
	People(int age, string name) :_age(age), _name(name)
	{}
//    拷貝構造函數
	People(const People& obj)
	{
		_age = obj._age;
		_name = obj._name;
		cout << "拷貝構造調用" << endl;
	}
	void Print()
	{
		cout << _name << ":" << _age<< endl;
	}
private:
	int _age;
	string _name;
};

int main()
{
	People xiaoMing(14, "小明");
	People xiaoHong(xiaoMing);
	xiaoMing.Print();
	xiaoHong.Print();
	return 0;
}

打印結果如下:

可以發現小明的屬性已經拷貝給了小紅,那麼如果我們刪除掉上述代碼中的拷貝構造函數,結果會怎麼樣?答案是:結果是一樣的,這是爲什麼呢,這是因爲系統會生成一個默認的拷貝構造函數,來進行對象初始化對象的操作,但是這種拷貝構造函數只能用於淺拷貝,不能用於深拷貝。接下來我們引出淺拷貝和身拷貝。

2.淺拷貝和深拷貝

假設類中的成員變量有一個指針,我們在類的構造函種爲這個指針在堆上申請了內存。如果我們用這個類取初始化其他類會發生什麼情況?

我們先來看一個實例:

class  People
{
	public:
	//構造函數
		People(int age, string name, int size = 3) :_age(age), _name(name),_size(size)
	{
			_arr = new int[size];
	}
		~People()
		{
			delete[] _arr;
		}
	void Print()
	{
		cout << _name << ":" << _age<< endl;
	}
private:
	int _age;
	string _name;
	int _size;
	int* _arr;
};

int main()
{
	People xiaoMing(14, "小明");
	People xiaoHong(xiaoMing);	
	return 0;
}

我們在People類中加了兩個變量,一個_size,一個int的指針,然後在析構函數種將_arr的內存釋放。執行會發現上述代碼會報錯,這是爲什麼呢?

如上圖,小明首先在堆上有三個字節的大小,通過一個默認的拷貝構造函數(淺拷貝),小紅的_arr指針也指向了這塊空間,當程序結束的時候,首先調用小明的析構函數釋放了這塊空間,被釋放掉了,小紅的析構函數又取釋放這塊被釋放掉的空間,所以程序會報錯。

總結一下:

淺拷貝只是複製指向某些對象的指針,並不會對所指內容進行復制。如上:淺拷貝只是把小名的_arr指針指向了小明的_arr指針,並沒又進行空間的賦值。當程序結束的時候,不同對象的析構函數會釋放同一塊內存,報錯。

而深拷貝,通過寫拷貝構造函數,使堆上的空間也複製一份,當程序結束的時候,不同對象的析構函數會釋放的內存雖然內容一樣,但是地址不一樣,所以不會報錯。

在上述的代碼種我們增加拷貝構造函數如下之後,程序不會報錯。

People(const People& obj)
{
	_age = obj._age;
	_name = obj._name;
	_size = obj._size;
	_arr = new int[_size];
	cout << "拷貝構造調用" << endl;

}

3.拷貝構造函數的參數能用值傳遞嗎?

右函數的傳參過程我們可以得知,如果函數的參數不是指針或者引用的時候,我們在傳參的時候,會將實參複製給形參。

如果我們將拷貝構造函數的參數去掉引用,那麼在調用拷貝構造函數的時候,首先會申請一個新的對象,然後用傳入的實參去初始化這個新的對象,這個時候還會調用到我們的拷貝構造函數,如此層層調用,形成無限的遞歸。

因此,拷貝構造函數的參數必須用引用或者指針。

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