對string深拷貝淺拷貝的理解剖析

2021-08-03 22:40:41 字數 4607 閱讀 1161

首先簡單談一下自己的理解:

淺拷貝,即在定義乙個類a

,使用類似

a a;  a a1(a);

或者a a1 = a; 

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

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

的拷貝建構函式:

class string  

;

string(const string &other)

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

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

class a  

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

} private:

char *m_data; // 一指標成員

};

int main()

執行結果:

*** 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;     // 一指標成員

};  

int main()    

下面是我在具體的應用中使用深拷貝的情況,現在把這個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, int iage)  

~recorder()  

*/}  

public:  

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

void* m_precvalue;//每條記錄的值

char *m_paddr;  

};  

int main()    

對比結果:

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

執行結果:

測試預設建構函式

預設 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.  乙個類中可以存在多個拷貝建構函式,例如:

calss a  

String類,淺拷貝,深拷貝

想要使用c 中的類那麼必須要有它的標頭檔案,include 首先來看下面乙個 這個程式很簡單,但是如果有乙個空指標呢?那麼就需要判斷了,並且用預設值把有引數的string和沒有引數的string合併在一起,那這個程式只需要做下面的改變 既然你的建構函式開闢了一段空間,那麼就需要釋放掉,此時就需要析構...

String類 (淺拷貝 深拷貝 寫時拷貝)

淺拷貝是指當物件的字段值被拷貝時,字段引用的物件不會被拷貝。例如,如果乙個物件有乙個指向字串的字段,並且我們對該物件做了乙個淺拷貝,那麼兩個物件將引用同乙個字串。存在問題 如果源程式中沒有顯示定義拷貝建構函式,在進行物件的拷貝時,將呼叫系統預設的拷貝建構函式,這就使得兩個物件指向了同一資源,而析構函...

淺拷貝 深拷貝

copy mutablecopy copy 不管是可變的,還是不可變的,結果都是不可變的 mutablecopy 不管是不可變的,還是可變的,結果都是可變的 nsmutablestring str nsmutablestring stringwithformat a nsarray arr1 str...