C 拷貝和賦值 淺拷貝和深拷貝詳解

2021-09-23 21:58:14 字數 3023 閱讀 1485

在c++primer中(p440)中闡述了乙個類中的拷貝控制操作有以下4類:

(1)拷貝建構函式

(2)拷貝賦值運算子

(3)析構函式

(4)移動建構函式

(5)移動賦值運算子

其中,(4)和(5)是新的c++標準特性。

這裡先討論拷貝建構函式和賦值運算子的區別,什麼時候會呼叫這兩個建構函式。

然後討論淺拷貝和深拷貝的區別。

class 是類名

拷貝建構函式:class(const class &);

賦值運算子:class& operator=(const class&);   

如果你認為兩者呼叫是按函式宣告呼叫的,那就錯了。

用下面程式簡單說明:

#include "stdafx.h"

#include // 拷貝和賦值的區別

class test;

test& operator=(const test&);

test(const test&)

~test(){};

};int main()

// 結果顯示:

執行了拷貝操作

執行了拷貝操作

執行了賦值操作

執行了賦值操作

請按任意鍵繼續. . .

分析:

(1)是不是覺得t3 = t1是執行了拷貝建構函式很奇怪?

那我們假設t3 = t1執行了賦值運算子會發什麼什麼。過載賦值運算子函式裡,會把t1裡的成員資料複製到自身(即t3),但問題來了,t3這個時候都沒有被例項化(即沒有構造),壓根沒有this指標,如何複製呢。

(2)test t2(t1) 和 test t3 = t1 其實還是有區別的

test t2(t1); 屬於直接初始化:要求編譯器使用普通的函式匹配來選擇與我們提供的引數最匹配的建構函式。

test t3 = t1; 屬於拷貝初始化:要求編譯器將右側運算物件拷貝到正在建立的物件中,如果需要還要進行型別轉換。

總結:

(1)拷貝構造是物件還沒有建立,而賦值時物件已經建立了。

(2)拷貝建構函式什麼時候會被呼叫:

為什麼拷貝建構函式的引數要為引用?

那我們假設拷貝建構函式裡的引數不為引用會發什麼什麼?執行 test t3 = t1 時,將t1傳遞給 test(test),因為不是引用,所以是值傳遞,值傳遞要拷貝,t1執行拷貝建構函式,如此就會陷入無限迴圈中。

為什麼賦值運算子要返回自身的引用(也就是返回乙個指向左側運算子物件的引用)?

鏈式操作,t5 = t4 = t1;

看以下兩段**

class test;

test(const test&)

test& operator=(const test&);

~test(){};

};int main()

結果顯示:

執行構造操作

請按任意鍵繼續. . .

class test;

test(const test&)

test& operator=(const test&);

~test(){};

};int main()

// 結果顯示:

執行構造操作

請按任意鍵繼續. . .

第一段**,test t = 5,由於建構函式中有test(int x),這個建構函式第乙個函式為int(後面可以有預設值引數),所以隱式轉換,這種叫轉移建構函式。我一直以為,當執行了建構函式後得到乙個臨時物件然後在通過拷貝建構函式將這個臨時物件傳給t,但是根據上面的**,此轉移建構函式相當於  test t(5)。

第二段**,加了explicit關鍵字,說明上面的隱式轉換不會發生,所以就要顯示使用。所以只能 test t(5)。

已知,如果乙個類沒有顯示建構函式、拷貝建構函式、賦值建構函式時,編譯器會預設提供乙個建構函式、拷貝建構函式、賦值建構函式。

至於,如果乙個類沒有顯示建構函式,編譯器會提供乙個合成的預設建構函式這句話是有問題的,只有當類需要編譯器提供預設建構函式才會提供。這裡就不具體展開了。。

淺拷貝和深拷貝的區別:前者就是使用編譯器提供的預設拷貝建構函式或者預設賦值建構函式。後者是自己顯示實現的拷貝/賦值建構函式。

大多時候,使用預設的拷貝/賦值建構函式就行了,但是當類中有動態分配的資料,或者類中有別的類成員時且該類中也有動態分配記憶體,然後也沒有顯示拷貝/賦值函式時....層次遞推,可能這個包含類中也包含了別的類。。。

這個時候如果用編譯器預設提供的拷貝和賦值建構函式 就會出現問題。

簡單用下面兩段**說明:

(1)使用預設的拷貝/賦值建構函式

class test;

test::test(int x, int y) : a(x), b(y)

test::~test()

int main()

// 結果報錯

(2)自定義拷貝建構函式

class test;

test::test(int x, int y) : a(x), b(y)

test::~test()

test::test(const test& other)

}int main()

// 結果顯示:

t1.a = 5 t1.b = 5

t2.a = 5 t2.b = 5

請按任意鍵繼續. . .

分析:**(1)為什麼會報錯,因為**1中的預設建構函式實現如下:

test::test(const test& other)
預設建構函式,只是將t1中的num指標賦值給t2,也就是t1和t2物件中的num共同指向相同的記憶體,但是main()函式結束時,要執行析構函式,t1執行一次,num釋放記憶體了,而t2又釋放已經不存在的記憶體,當然會報錯!

所以,自己定義類的時候一定要自己顯示拷貝/賦值建構函式!!!。。什麼時候執行拷貝建構函式,上面已經說了。

淺拷貝 深拷貝和淺賦值 深賦值

include includeusing namespace std class string else 淺拷貝 也就是系統預設的拷貝,可寫可不寫。string const string s 預設的拷貝構造 深拷貝 string const string s string s2 s1 深賦值 str...

賦值 淺拷貝和深拷貝

三種方法的形式存在一定的類似,但是也存在各個之間不相同的地方。淺拷貝和深拷貝是用在物件 object 或者陣列 array 這樣的資料型別拷貝賦值時候的說法,而賦值操作也可以用在基礎的資料型別,如number string等 賦值 對於物件型別資料的影響 賦於該值在棧中的位址,而不是堆中的資料,使得...

python賦值,淺拷貝和深拷貝

a 1 b a a 2 a 2 b 1 你將1賦給a變數,然後拷貝a中的值,將它賦給變數b。當稍 後將a中的值改變為2時,這不會影響b中的值。這是因為a和b 是不同的變數,儲存了不同的值。但列表不是這樣的。當你將列表賦給乙個變數時,實際上是將列表的 引用 賦給了該變數。引用是乙個值,指向某些資料。列...