C語言拾遺:位拷貝與值拷貝,淺拷貝與深拷貝

先從一個問題講起


struct sct
{
	int i;
	double d;
};

結構體sct A與結構體sct B能構通過=號直接賦值嗎?

即B = A有意義嗎?

如果有意義,執行完語句後,是B只有A的地址,當A改變時B即改變,還是B具有獨立的內存空間?

由此引出了幾個概念,位拷貝與值拷貝,淺拷貝與深拷貝。

資料確實不好找,我先寫下一些。

位拷貝與值拷貝待整理,是否淺拷貝就是位拷貝,深拷貝就是值拷貝,有待歸納。


淺拷貝就是把對象(結構)裏的值完全複製給了另一個對象(結構)。如 A = B; 就是把B中的成員變量的值完全給A拷貝了一份。而由於是完全照搬的拷貝,它不夠智能,所以對於地址也是完全照抄。如果B中有一個成員變量指針已經申請了內存那A中的那個同樣的成員變量也指向同一個塊內存不會在堆內再申請一塊內存。這樣的問題就是 當B把內存釋放了那A內的指針就是野指針了(也就是說這個指針是亂指的,有可能就會因爲訪問到危險的區域而導致程序崩潰)。類的默認複製構造函數是將類中的非靜態成員變量逐個複製,是淺拷貝。

深拷貝,這要求根據指針指向的值,重新申請內存,再依次將指針指向的值複製過來,有相互獨立的內存空間。比如B中有int i = new int[30],要把B按深拷貝複製給A,則應該先在A中申請內存int i = new int[30],再用for循環或其它方法把內存裏的值完全拷貝。這一般是需要你手動去做的。一般好的複製構造函數(要自己寫)及某些函數(strcpy等),都是深拷貝。所以在自己寫複製構造函數時,就應該寫成深拷貝。


char * a = "windows";
char * b = "linux";
b = a;

如上代碼,執行完後,b的值爲"windows",但是相應的,b的地址其實與a相同。即b實際上是a的一個別名。這會產生很多你不希望產生的情況,比如a被刪除或改動,則b也會改變。

可見深拷由與淺拷由主要是針對有指針的數據來說的,對於兩個int類型的數據來說,不存在淺拷貝深拷貝之說。


實際上,ANSI C允許結構體賦值,而C++允許類對象賦值。而這裏所說的賦值都是不夠智能的淺拷貝。對於C++,使用默認的潛拷貝往往會造成一系列問題(參見C++ Primer Plus 類和動態內存分配部分),所以,一般可以如下重載賦值操作。

ClassName & ClassName::operator=(const ClassName & cls)
{
	if (this == &cls)
		return *this;
	//刪除使用new創建的數據
	//根據cls重新new數據進行復制
	return *this;
}


再回到題目來,可以看到結構體是可以直接通過=賦值的。

#include <cstdio>
#include <iostream>

using namespace std;

struct sct
{
	int i;
	double d;
	char ch[6];
};

void show (sct &A, sct &B)
{
	cout << "A的地址:" << &A << "\tA.i=" << A.i << "\tA.d=" << A.d << "\tA.ch=" << A.ch << endl;
	cout << "B的地址:" << &B << "\tB.i=" << B.i << "\tB.d=" << B.d << "\tB.ch=" << B.ch <<endl;
}

int main ()
{
	sct A = {5, 8.2, "hello"};
	sct B = {0, 0.0, "nihao"};
	cout << "struct sct" << endl << "{" << endl << "int i;\ndouble d;\nchar ch[6];\n" << "}" << endl;
	cout << "原數據" << endl;
	show(A, B);
	B = A;
	cout << "執行B = A" << endl;
	show(A, B);
	B.i += 10;
	cout << "執行B.i += 10" << endl;
	show(A, B);
	system("pause");
}


但是當結構體或類中存在指針時,就要注意了,可能導致兩個結構體裏的數據指針相同,當你對一個結構體裏指針指向的數據進行修改,另一個結構體內指針指向的數據自然也修改了,這將導致你不想要的事情發生。而結構體中進行了動態內存分配時候,相同道理,也是你所不想的。

如下情況,則有淺拷貝與深拷貝之別:


其中ch[6]的數據類型爲char型數組,佔用空間爲1*6個字節,所以默認執行淺拷貝時,進行1*6個字節的完全拷貝,因而有自己獨立的內存空間,值也得到拷貝。

而其中pch的數據類型爲char型指針,它佔用的空間爲4*1個字節,所以默認執行淺拷貝時,進行4*1個字節的完全拷貝,因而其實得到拷貝的只是地址,當A.pch發生改變時,B.pch也會發生你所不想發生的情況。


主要參考資料:

http://blog.csdn.net/liam1122/article/details/1966617

http://wenku.baidu.com/link?url=PKgI5DMEobtUtLtN3kZclSwZnkdg-FqOKtjvGm6rZrkY9rb6NqGaNj3xy2aDM4MKvei3b2OCEgEqvgQPJdOAMrxRpitYiqXb0L2HuyDk8_q


發佈了39 篇原創文章 · 獲贊 12 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章