首先簡單談一下自己的理解:
淺拷貝,即在定義乙個類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...