深拷貝和淺拷貝的區別

2021-08-17 14:24:02 字數 4755 閱讀 2437

淺拷貝,即在定義乙個類

a,使用類似

a obj;  a obj1(obj);

或者a obj1 = obj; 

時候,由於沒有自定義拷貝建構函式,

c++編譯器自動會產生乙個預設的拷貝建構函式。這個預設的拷貝建構函式採用的是「位拷貝」(淺拷貝),而非「值拷貝」(深拷貝)的方式,如果類中含有指標變數,預設的拷貝建構函式必定出錯。

用一句簡單的話來說就是淺拷貝,只是對指標的拷貝,拷貝後兩個指標指向同乙個記憶體空間,深拷貝不但對指標進行拷貝,而且對指標指向的內容進行拷貝,經深拷貝後的指標是指向兩個不同位址的指標。

淺拷貝會出現什麼問題呢?

假如有乙個成員變數的指標,char *m_data;

其一,淺拷貝只是拷貝了指標,使得兩個指標指向同乙個位址,這樣在物件塊結束,呼叫函式析構的時,會造成同乙份資源析構2

次,即delete

同一塊記憶體

2次,造成程式崩潰。

其二,淺拷貝使得obj.m_data

和obj1.m_data

指向同一塊記憶體,任何一方的變動都會影響到另一方。

其三,在釋放記憶體的時候,會造成obj1.m_data

原有的記憶體沒有被釋放(這句話,剛開始我不太理解,如果沒有走自定義的拷貝建構函式申請記憶體空間,a obj1(obj);

也不走預設建構函式,走的是預設的拷貝建構函式,何來分配空間這一說,更不會造成

obj1.m_data

原有的記憶體沒有被釋放,這裡剛開始我一直有疑問),造成記憶體洩露。

事實是這樣的,當delete obj.m_data, obj.m_data

記憶體被釋放後,由於之前

obj.m_data

和obj1.m_data

指向的是同乙個記憶體空間,

obj1.m_data

所指的空間不能在被利用了,

delete obj1.m_data

也不會成功,一致已經無法操作該空間,所以導致記憶體洩露。

深拷貝採用了在堆記憶體中申請新的空間來儲存資料,這樣每個可以避免指標懸掛。

下面來看看類string

的拷貝建構函式

[cpp]view plain

copy

class

string  

;    

string(const

string &other)  

可以看到在拷貝建構函式中為成員變數申請了新的記憶體空間,這就使得兩個物件的成員變數不指向同乙個記憶體空間,除非你的確需要這樣做,用於實現一些其他的用途。

淺拷貝:也就是在物件複製時,只是對物件中的資料成員進行簡單的賦值,如果物件中存在動態成員,即指標,淺拷貝就會出現問題,下面**:

[cpp]view plain

copy

#include 

class

a    

~a()     // 析構函式,釋放動態分配的空間

}  private

:  char

*m_data;     

// 一指標成員

};  

intmain()  

執行結果:

*** glibc detected *** ./******: double free or corruption (fasttop): 0x000000000c62a010 ***

分析:由於沒有拷貝建構函式,走編譯器預設的拷貝建構函式,a b(a); 

進行物件析構時,會造成釋放同一記憶體空間

2次,導致記憶體洩露。

深拷貝:對於深拷貝,針對成員變數存在指標的情況,不僅僅是簡單的指標賦值,而是重新分配記憶體空間,如下:

[cpp]view plain

copy

#include 

#include 

class

a    

a(const

a& r)  

~a()     // 析構函式,釋放動態分配的空間

}  private

:  char

*m_pdata;     

// 一指標成員

};  

intmain()  

下面是我在具體的應用中使用深拷貝的情況,現在把這個demo

貼出來:

[cpp]view plain

copy

#include 

#include 

#include 

#include 

using

namespace

std;  

/*儲存記錄資訊的結構體*/

typedef

struct

_recoder_value_stru  

recoder_value_stru;  

class

recorder  

//拷貝建構函式

/*              recorder(const recorder &recorder)

*///建構函式

recorder(int

iid, 

intiage)  

~recorder()  

*/}  

public

:  recoder_value_stru m_stru_recvalue;//儲存記錄資訊的結構體 

void

* m_precvalue;

//每條記錄的值

char

*m_paddr;  

};  

intmain()  

對比結果:

注釋掉自定義拷貝建構函式,

執行結果:

測試預設建構函式

預設 construct recorder->&m_stru_recvalue: ddbb8de0,     m_precvalue: ddbb8de0   m_paddr: 1b8a0010

非參:btest ->&m_stru_recvalue: ddbb8de0         addr: ddbb8de0  m_paddr: 1b8a0010

非參:btest1->&m_stru_recvalue: ddbb8dc0         addr: ddbb8de0  m_paddr: 1b8a0010

測試帶引數的建構函式

construct recorder->&m_stru_recvalue: ddbb8da0   m_precvalue: ddbb8da0   m_paddr: 1b8a0080

帶參:btest2->m_stru_recvalue: ddbb8da0  m_precvalue: ddbb8da0  , m_paddr: 1b8a0080

帶參:btest3->m_stru_recvalue: ddbb8d80  m_precvalue: ddbb8da0  , m_paddr: 1b8a0080

預設拷貝建構函式結果分析:

通過結果可以看出,當成員變數為指標變數的時候,指標成員變數指向的位址都是同乙個位址,無論是申請空間的成員變數m_precvalue

,和僅僅作為指標賦值的成員變數

m_paddr

;結構體的位址是變化的,除指標外其他成員淺拷貝與深拷貝沒什麼區別。

開啟自定義拷貝建構函式,執行結果:

測試預設建構函式

預設 construct recorder->&m_stru_recvalue: 58bb9e20,     m_precvalue: 58bb9e20   m_paddr: 7a2c010

拷貝 construct recorder->&m_stru_recvalue: 58bb9e00      m_precvalue: 58bb9e00   m_paddr: 7a2c080

非參:btest ->&m_stru_recvalue: 58bb9e20         addr: 58bb9e20  m_paddr: 7a2c010

非參:btest1->&m_stru_recvalue: 58bb9e00         addr: 58bb9e00  m_paddr: 7a2c080

測試帶引數的建構函式

construct recorder->&m_stru_recvalue: 58bb9de0   m_precvalue: 58bb9de0   m_paddr: 7a2c0f0

拷貝 construct recorder->&m_stru_recvalue: 58bb9dc0      m_precvalue: 58bb9dc0   m_paddr: 7a2c160

帶參:btest2->m_stru_recvalue: 58bb9de0  m_precvalue: 58bb9de0  , m_paddr: 7a2c0f0

帶參:btest3->m_stru_recvalue: 58bb9dc0  m_precvalue: 58bb9dc0  , m_paddr: 7a2c160

自定義深拷貝建構函式結果分析:

從結果可以看出,所有成員變數的位址都不相同。

其他:1. 有時候為了防止預設拷貝發生,可以宣告乙個私有的拷貝建構函式(不用寫**),這樣的話,如果試圖呼叫 a  b(a); 就呼叫了私有的拷貝建構函式,編譯器會報錯,這也是一種偷懶的做法。

2.  乙個類中可以存在多個拷貝建構函式,例如:

[cpp]view plain

copy

calss a  

深拷貝和淺拷貝的區別

retain屬性表示兩個物件位址相同 建立乙個指標,指標拷貝 內容當然相同,這個物件的retain值 1也就是說,retain 是指標拷貝 淺拷貝 copy 是內容拷貝 深拷貝 ios中並不是所有的物件都支援copy,mutablecopy,遵守nscopying 協議的類可以傳送copy訊息,遵守...

深拷貝和淺拷貝的區別

一.深拷貝 源物件和副本物件是不同的兩個物件 源物件引用計數不變,副本物件計數器為1 因為是新產生的 至少有一層是物件複製 二.淺拷貝 源物件和副本物件是同乙個物件 源物件 副本物件 引用計數器 1,相當於做一次retain操作 本質 沒有產生新的物件 三.完全複製 對於被複製的物件每一層都是物件複...

深拷貝和淺拷貝的區別

深淺拷貝的區別 淺拷貝是將原始物件中的資料型字段拷貝到新物件中去,將引用型字段的 引用 複製到新物件中去,不把 引用的物件 複製進去,所以原始物件和新物件引用同一物件,新物件中的引用型字段發生變化會導致原始物件中的對應欄位也發生變化。深拷貝是在引用方面不同,深拷貝就是建立乙個新的和原始欄位的內容相同...