基本概念:
深拷貝:
將乙個物件拷貝給另乙個物件的時候,被賦值的物件儲存賦值物件的乙個額外副本。若類成員中含有指標成員,且用new初始化的時候,被賦值的成員,會申請一塊記憶體,將賦值物件的指標成員所指的記憶體的內容複製到這塊記憶體中。兩個指標各自指向自己申請的記憶體。
淺拷貝:
和深拷貝相似,淺拷貝對於非指標成員都是直接賦值。但是當類成員中含指標成員,且用new初始化的時候,被賦值的成員指標並不會額外申請一塊記憶體,而僅僅是將自己指向賦值物件的指標成員所指的那塊記憶體。兩個指標指向同一塊記憶體
。當我們沒有定義類的複製建構函式和賦值運算子時,編譯器會生成預設的版本,它們使用淺拷貝。
回到上面所說的,為什麼我們需要定義"深拷貝"的複製建構函式和賦值運演算法捏 ? 難道,是因為預設的淺拷貝會導致錯誤 ?
沒錯! 我們知道,如果定義的類中含指標成員,如果它將會使用new申請新記憶體。在析構函式中,我們會用delete釋放相應的記憶體占用。
考慮兩種情況:
1. 乙個物件使用另外乙個已有物件初始化,這樣將呼叫預設複製建構函式(有可能還會呼叫賦值操作符,視編譯器而定)。由於使用淺拷貝,就會存在這兩個物件的指標成員指向同一塊記憶體的情況,當這兩個物件棄用時,會呼叫它們的析構函式。這樣會出現同一塊記憶體被釋放兩次的情況,出現未知的錯誤。
類似地,如果你定義了乙個返回物件的函式,也會造成同一塊記憶體釋放兩次的情況,為啥 ? 因為這還將呼叫複製建構函式,按值傳遞意味著建立原始變數的乙個副本。caller和這個函式(callee)中的物件的指標指向同一塊記憶體。當函式返回的時候,函式中的這個物件要被kill掉,呼叫析構函式了,釋放掉占用的記憶體... 放心,這些都不會告訴你的。嗯,當caller中的那個物件析構時,那塊記憶體又被釋放了一次... 仍然是不可預知的錯誤。類似地,建立臨時物件的時候,也會呼叫複製建構函式,這將發生同樣的趣事--同樣的奇怪的錯誤。
2.兩個已有物件之間的賦值,這將呼叫預設賦值運算子函式。後面的情況和1相同,都是淺拷貝鬧的--兩個物件的指標指向同一塊記憶體,然後被釋放兩次。
「當定義的類中含有指標成員,且使用常規new(或定位new,定位在申請的堆記憶體中)初始化的時候,需定義深拷貝的複製建構函式和賦值操作符」,不然會被外星人抓走。
圖示:
**示例:
c++ code
//淺拷貝 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include
#include
using
namespace std;
class person
~person()
};
int main()
c++ code
//深拷貝 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include
#include
using
namespace std;
class person
;
person::person(
char *pn)
person::person(
const person &p)
person::~person()
void main()
C 拷貝建構函式 深拷貝,淺拷貝
對於普通型別的物件來說,它們之間的複製是很簡單的,例如 int a 88 int b a 而類物件與普通物件不同,類物件內部結構一般較為複雜,存在各種成員變數。下面看乙個類物件拷貝的簡單例子。執行程式,螢幕輸出100。從以上 的執行結果可以看出,系統為物件b分配了記憶體並完成了與物件a的複製過程。就...
C 拷貝建構函式 深拷貝,淺拷貝
對於普通型別的物件來說,它們之間的複製是很簡單的,例如 int a 88 int b a 而類物件與普通物件不同,類物件內部結構一般較為複雜,存在各種成員變數。下面看乙個類物件拷貝的簡單例子。include using namespace std class cexample void show 執...
C 拷貝建構函式 深拷貝,淺拷貝
c 拷貝建構函式 深拷貝,淺拷貝 對於普通型別的物件來說,它們之間的複製是很簡單的,例如 int a 88 int b a 而類物件與普通物件不同,類物件內部結構一般較為複雜,存在各種成員變數。下面看乙個類物件拷貝的簡單例子。iostream using namespace std class ce...