1. 程式人生 > >C++中的深拷貝與淺拷貝

C++中的深拷貝與淺拷貝

淺拷貝問題丟擲:

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;

/*
  淺拷貝問題丟擲
*/
class ShallowCopy
{
public:
	ShallowCopy(const char * myp)
	{
		len = strlen(myp);
		p = (char *)malloc(len + 1); //指標指向誰,就把誰的地址給指標。把記憶體空間首地址給p,+1是\0,沒有的話會記憶體越界。
		strcpy(p, myp);//把myp指標所指向的記憶體空間的資料copy到p所指向的記憶體空間。
	}
	
	~ShallowCopy()
	{
		if ( p != NULL )
		{
			free(p);
			p = NULL;
			len = 0;
		}
	}
private:
	char * p;
	int len;
};
void ObjPlaymain()
{
	ShallowCopy  obj1("abcdefg");
	/*
	   呼叫obj2類的copy建構函式,沒寫,呼叫c++編譯器提供的copy建構函式(淺拷貝)
	   c++編譯器偷懶,淺拷貝,把obj1的屬性拷貝到obj2,只把指標變數的值拷貝過來了,沒有另外開闢記憶體。
	   */
	ShallowCopy obj2 = obj1;
}
void main()
{
	ObjPlaymain();
	system("pause");
}

這樣程式會出現coredump, 可以用如下記憶體圖理解:
在這裡插入圖片描述
c++編譯器預設的copy建構函式,淺拷貝,把obj1的屬性拷貝到obj2,只把指標變數的值拷貝過來了,沒有另外開闢記憶體。這樣obj1和obj2的p指標指向同一塊記憶體空間。
析構的時候,先析構obj2,把那塊記憶體空間析構掉了,同時把記憶體空間置成NULL,把len置成0。然後析構obj1,此時那塊記憶體空間是垃圾值(已經析構過了),則此時的obj1的指標p是個野指標,呼叫obj1的解構函式,同一塊記憶體空間被析構了2次。析構的時候程式會出現coredump。
那怎麼辦呢?
藍色箭頭是我們的解決方案。既然c++編譯器提供的copy建構函式不行,我們就手動編寫copy建構函式唄,讓2個指標不要指向同一塊記憶體,給它重新開闢。所以必要的時候,必須手工編寫copy建構函式。
下面是解決方案:

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;

/*
  淺拷貝問題解決:不使用c++編譯器提供的copy建構函式,手工編寫copy建構函式,使用深拷貝。
*/
class DeepCopy
{
public:
	DeepCopy(const char * myp)
	{
		len = strlen(myp);
		p = (char *)malloc(len + 1 ); 
		strcpy(p, myp);
	}

	DeepCopy(const DeepCopy & obj1)
	{
		len = strlen(obj1.p);
		p = (char *)malloc(len + 1);
		strcpy(p, obj1.p);
	}

	~DeepCopy()
	{
		if (p != NULL)
		{
			free(p);
			p = NULL;
			len = 0;
		}
	}
private:
	char * p;
	int len;
};
void ObjPlaymain()
{
	DeepCopy  obj1("abcdefg");
	DeepCopy obj2 = obj1;
}
void main()
{
	ObjPlaymain();
	system("pause");
}

這樣就不會出現問題了,ok!

之前說過初始化和賦值不一樣,如果用=連線obj1和obj2,又會怎樣呢?

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;

/*
  =也不行,需要過載操作符
*/
class DeepCopy
{
public:
	DeepCopy(const char * myp)
	{
		len = strlen(myp);
		p = (char *)malloc(len + 1);
		strcpy(p, myp);
	}

	~DeepCopy()
	{
		if (p != NULL)
		{
			free(p);
			p = NULL;
			len = 0;
		}
	}
private:
	char * p;
	int len;
};
void ObjPlaymain()
{
	DeepCopy  obj1("abcdefg");
	DeepCopy obj2("obj2");
	/*
	  =操作,把obj1的屬性copy給obj2,用c++預設的copy建構函式,
	  和剛才淺拷貝一樣,析構時出現問題,需要過載操作符=
	*/
	obj2 = obj1;
}
void main()
{
	ObjPlaymain();
	system("pause");
}

結果發現,和剛才一樣,也會出現同樣的問題。
依舊用記憶體圖解釋:
在這裡插入圖片描述
人家obj1和obj2本來指向不同的記憶體空間,各不干擾。結果你"obj2=obj1"一下,把obj1的值賦值給obj2,obj2的p和len和obj1一模一樣,把人家obj2的p指標活生生指到obj1那塊記憶體空間了,結果造成obj1和obj2的指標又指向同一塊記憶體空間了,析構的時候又出現coredump。而且,原來人家obj2的0xbb11那塊記憶體被暴露出來了,記憶體洩露了。
這個"="也太膚淺了吧,看來需要操作符過載嘍!