對深拷貝與淺拷貝的再次理解

2021-07-10 01:37:26 字數 4763 閱讀 8502

... ...

先說下自己的理解吧,淺拷貝,即在定義乙個類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    

... ...

**:

對深拷貝與淺拷貝的再次理解

記得11年底找工作的時候,面試時曾經遇到有面試官問的對深拷貝與淺拷貝的理解,那時候自己回來查了資料,寫了篇部落格,感覺自己理解了,其實理解的不深刻,最近在除錯 bug的時候,再次遇到深拷貝與淺拷貝,認真分析了,寫寫自己的心得吧。先說下自己的理解吧,淺拷貝,即在定義乙個類a 使用類似 a obj a ...

淺拷貝 深拷貝的再次理解

以前對淺拷貝的認識不夠,認為只是對指標的賦值,沒有真實的開闢空間。看了一篇部落格後感覺自己的認識還不夠,其實淺拷貝還會導致記憶體洩漏!對,你沒看錯,確實是 記憶體洩漏!最後會把部落格鏈結貼出來。我們以前對淺拷貝深拷貝的理解是這樣的 淺拷貝是對指標的拷貝,拷貝以後兩個指標指向的是同一塊記憶體空間,深拷...

Python 淺拷貝與深拷貝 再次整理

coding utf 8 import copy def print id lst lst id id y for y in lst lst id.extend id y for y in lst 1 return str lst id def test copy anndy anndy age 2...