C 拷貝構造器之淺拷貝與記憶體重析構

2021-07-28 08:48:12 字數 3412 閱讀 2052

一般構造器、析構器、拷貝構造器會被稱之為建構函式、析構函式與複製建構函式,但是其與函式還是有一些區別,所以我們以「器」來稱呼之。

(1)、構造器簡析:

constructor 構造器* 與類名相同a(),(a為類名),無返回值,生成物件時系統自行呼叫,相當於初始化

* 可以有引數,能夠過載、以及設定預設引數

* 如果不自定義任何構造器,則系統會有乙個預設的無參構造器生成,若自定義了乙個構造器,則系統不再生成構造器

* 注意:無引數構造器與有引數但是有預設引數的構造器,定義物件時可能會產生二義性

* 所以過載與預設引數不要在同乙個函式名中同時出現

(2)、析構器簡析:

destructor 析構器* 格式:~a(),無引數、無返回值、用於物件銷毀時的記憶體處理工作(物件消失時,自動被呼叫)

* 所謂消失,指的是跳出其作用空間,且以後不會再使用

* 若無自定義析構器,則系統預設生成乙個析構器

* 對於系統自行析構的,先建立的後析構、後建立的先析構

* 由於析構器無引數,所以不存在過載的問題

(3)、拷貝構造器:

copy constructor 拷貝構造器* 格式:a(const a &);

* 若不自定義,則採用系統預設的拷貝構造器(類似於構造器)

* 系統提供的拷貝構造器,預設是乙個等位拷貝,即江湖上傳聞的「淺拷貝」,淺拷貝可能會導致記憶體重析構(double free),但是記憶體重析構在有些系統上可能不會表現出來(如windows)。

* 在有些情況(物件含有堆空間的時候),要自定義實現拷貝構造器

* 拷貝構造是乙個從無到有的過程(用乙個已有的物件,完成另乙個物件從無到有並初始化的過程),而構造是乙個從有到初始化的過程

eg:(注意一下幾種寫法的區別)

string

a("china");//構造

string b = a

;//拷貝構造

string c(a);//拷貝構造

string d;//構造

d = a

;//賦值運算子過載

所謂記憶體重析構,其實就是同一塊堆記憶體被釋放了兩次,第一次釋放沒有什麼問題,但是第二次屬於free(null);的操作,這種記憶體重析構(重複析構)存在邏輯性的致命錯誤。

而淺拷貝由於只存在乙份堆空間,卻存在兩個物件擁有指向該空間的指標,所以析構時,兩個指標指向的空間都會被**,但是第二次**,指標null無指向、無效。但深拷貝析構時,各自物件有各自的堆記憶體,就不會存在這種重析構出錯的情況(如下圖)。(淺拷貝存在的這種問題在有的平台上或許不會變現出來)

以例項(string類的拷貝構造器)說明拷貝構造器的淺拷貝與記憶體重析構:

再將淺拷貝注釋掉,用深拷貝來編譯執行(結果正常):

其實每乙個類中,系統不但預設存在以上三種「器」,還存在預設的賦值運算子過載:

(預設)賦值運算子過載:* 格式:a& operator=(a&);

* 系統/編譯器提供預設過載,

* 預設賦值運算子過載也是一種等位賦值(淺賦值),若是自定義,編譯器不再提供

* 淺複製不但會導致重析構,還會導致自身記憶體洩漏;

* 對於自賦值需要特別注意。

為什麼說它是穿著馬甲的淺拷貝?又為什麼說預設賦值運算子過載會導致記憶體重析構與記憶體洩漏呢?我們畫張圖來分析:

/*系統預設的類似於如下所示*/

mystring& mystring::operator=(const mystring & another)

首先預設的賦值運算子過載是淺拷貝才會導致記憶體洩露,其次物件析構時會產生重析構。對於賦值運算子過載,在物件含有堆空間的申請與釋放時,也需要自行定義,不能夠使用系統預設的。對於我們mystring的例子來說,其非預設實現如下:

mystring& mystring::operator=(const mystring & another)

上面所說自賦值邏輯錯誤即:如果s1 = s1;那麼預設按(先刪除自身->再求長度->建立新空間->複製)的步驟走肯定是不對的,因為第一步刪除s1後,後面的步驟丟失掉了依賴,即所謂邏輯出錯。

即使是淺拷貝這種致命的邏輯錯誤,在windows下也可能不會有異常:

但是對於這種潛在的隱患,我們一定要注意,在物件有堆空間時,一定要用深拷貝,而不能用淺拷貝。對於c++的在這種淺拷貝的問題是因為char*引起來的,也就是為了相容c而引起的。在純c++語法編寫的程式中,由於不會用到char陣列、char*等,就不會有淺拷貝深拷貝的問題了。

拷貝建構函式之深拷貝與淺拷貝

若採用系統預設的拷貝建構函式,為淺拷貝,對於指著型資料,會存在共同指向乙個位址空間,若p2析構時刪除了位址空間,那麼p1析構時,系統就會崩潰 所以要用new重新開闢乙個位址空間,成為深拷貝 include stdafx.h include using namespace std class pers...

C 拷貝建構函式之深拷貝 淺拷貝

對於普通型別的物件來說,它們之間的複製是很簡單的,例如 int a 88 int b a 而類物件與普通物件不同,類物件內部結構一般較為複雜,存在各種成員變數。下面看乙個類物件拷貝的簡單例子。執行程式,螢幕輸出100。從以上 的執行結果可以看出,系統為物件b分配了記憶體並完成了與物件a的複製過程。就...

拷貝構造 深拷貝與淺拷貝

拷貝建構函式用乙個已有同類物件的資料對正在建立的物件進行資料初始化 c 為類提供預設版本的拷貝建構函式 預設複製建構函式可以完成物件的資料成員值簡單的複製 物件的資料資源是由指標指示的堆時,預設複製建構函式僅作指標值複製,即為淺拷貝 class teachert 顯示的提供乙個拷貝建構函式,來完成深...