深拷貝和淺拷貝

2021-07-29 17:36:38 字數 1841 閱讀 1219

很多時候在我們都不知道拷貝建構函式的情況下,傳遞物件給函式引數或者函式返回物件都能很好的進行,這是因為編譯器會給我們自動產生乙個拷貝建構函式,這就是「預設拷貝建構函式」,這個建構函式很簡單,僅僅使用「老物件」的資料成員的值對「新物件」的資料成員一一進行賦值,它一般具有以下形式:

注意:上述預設拷貝建構函式是編譯器自動生成的,我們不用編寫。但是,編譯器提供的預設拷貝建構函式不能解決所有的物件拷貝問題。

舉例:

這段**對前面的類,加入了乙個靜態成員,目的是進行計數。在主函式中,首先建立物件rect1,輸出此時的物件個數,然後使用rect1複製出物件rect2,再輸出此時的物件個數,按照理解,此時應該有兩個物件存在,但實際程式執行時,輸出的都是1,反應出只有1個物件。此外,在銷毀物件時,由於會呼叫銷毀兩個物件,類的析構函式會呼叫兩次,此時的計數器將變為負數。

產生錯誤的原因,就是拷貝建構函式沒有處理靜態資料成員

出現這些問題最根本就在於在複製物件時,計數器沒有遞增,我們重新編寫拷貝建構函式,如下:

在這段**執行結束之前,會出現乙個執行錯誤。原因就在於在進行物件複製時,對於動態分配的內容沒有進行正確的操作。我們來分析一下:

在執行定義rect1物件後,由於在建構函式中有乙個動態分配的語句,因此執行後的記憶體情況大致如下:

在使用rect1複製rect2時,由於執行的是淺拷貝,只是將成員的值進行賦值,這時 rect1.p = rect2.p,也即這兩個指標指向了堆裡的同乙個空間,如下圖所示:

當然,這不是我們所期望的結果,在銷毀物件時,兩個物件的析構函式將對同乙個記憶體空間釋放兩次,這就是錯誤出現的原因。我們需要的不是兩個p有相同的值,而是兩個p指向的空間有相同的值,解決辦法就是使用「深拷貝」。

在「深拷貝」的情況下,對於物件中動態成員,就不能僅僅簡單地賦值了,而應該重新動態分配空間,如上面的例子就應該按照如下的方式進行處理:

此時,在完成物件的複製後,記憶體的乙個大致情況如下:

此時rect1的p和rect2的p各自指向一段記憶體空間,但它們指向的空間具有相同的內容,這就是所謂的「深拷貝」。

通過對物件複製的分析,我們發現物件的複製大多在進行「值傳遞」時發生,這裡有乙個小技巧可以防止按值傳遞——宣告乙個私有拷貝建構函式甚至不必去定義這個拷貝建構函式,這樣因為拷貝建構函式是私有的,如果使用者試圖按值傳遞或函式返回該類物件,將得到乙個編譯錯誤,從而可以避免按值傳遞或返回物件。

深拷貝和淺拷貝

淺拷貝就是物件的資料成員之間的簡單賦值,如你設計了乙個沒有類而沒有提供它的複製建構函式,當用該類的乙個物件去給令乙個物件賦值時所執行的過程就是淺拷貝,如 class a a private int data int main 這一句b a 就是淺拷貝,執行完這句後b.data 5 如果物件中沒有其他...

淺拷貝和深拷貝

以下情況都會呼叫拷貝建構函式 乙個物件以值傳遞的方式傳入函式體 例如 已知class a,class b void func a a void func a a func b b 此時函式對b的操作是呼叫拷貝建構函式後的臨時拷貝物件。多數傳指標 乙個物件以值傳遞的方式從函式返回 如 return b...

深拷貝和淺拷貝

ios提供了copy和mutablecopy方法,顧名思義,copy就是複製了乙個imutable的物件,而mutablecopy就是複製了乙個mutable的物件。以下將舉幾個例子來說明。1 系統的非容器類物件 這裡指的是nsstring nsnumber等等一類的物件。nsstring stri...