筆記十 複製建構函式 深拷貝 淺拷貝

2021-07-13 03:27:41 字數 3812 閱讀 6379

定義:

只有單個形參,而且該形參是對本類型別物件的引用(常用const修飾),這樣的建構函式成為複製建構函式。複製建構函式可用於:

1、根據另乙個同型別的物件顯示或隱式初始化乙個物件

2、複製乙個物件,將它作為實參傳遞給乙個函式

3、從函式返回時複製乙個物件

4、初始化順序容器中的元素

5、根據元素初始化列表初始化陣列元素

——以上定義來自《c++ primer 中文版 第4版》

第一條中,若乙個自定義類物件已經初始化了,並且用該類去初始化另乙個同類型別的物件,假設類中存在指標型變數,若沒有顯示的構造複製建構函式,那麼編譯器會自動生成乙個複製建構函式,此時的複製稱為淺拷貝。

更準確的定義如下:

在c++中,在用乙個物件初始化另乙個物件時,只複製了成員,並沒有複製資源,使兩個物件同時指向了同一資源的複製方式稱為淺複製。

那麼淺拷貝存在的風險是什麼呢?

若自定義類中存在指標成員函式,那麼:

1、淺拷貝只是複製了指標,使得2個指標指向了同乙個位址,這樣在物件塊結束呼叫函式析構時,會造成對同一資源的2次析構,即delete 2次,引起程式崩潰。

2、淺拷貝使得2個指標成員指向同乙個記憶體位址,修改其中乙個指標指向的值,會改變另乙個物件的指標指向的值

3、在記憶體釋放時,作為實參傳遞的類由於析構不成功,造成記憶體洩露。

以上來自

通過乙個例項來反應:

**:

#include

using

namespace

std;

class a

~a()

private:

char* data;

};int main(int argc, char* argv)

針對以上**,進行單步,逐語句的除錯:

1、在a a;處設定斷點,採取逐語句f11除錯,進入到自定義建構函式中:

2、對於物件a——動態分配乙個記憶體給data,此時data的值為0x004084f8,儲存data這個值得記憶體位址為0x002ffbf4 :

3、繼續執行f11,會發現程式不會再次進入到自定義建構函式,因為此時a b(a)執行的複製建構函式,由於類沒有顯示定義,故由編譯器自動完成,程式執行到:

4、在main函式結束之前,由於物件的宣告週期已到,故此時需呼叫析構函式,且析構函式的順序是由後往前析構,即物件b在物件a之後定義,那麼b在a之前析構。通過觀察也可以判斷析構順序,此時儲存data變數的位址為0x002ffbe8 與a中儲存data的位址不同。

但是a, b中data值均為0x004084f8

5、delete釋放掉記憶體資源後,data的值變為0x004084f8《字串中的字元無法…>:(個人理解,這裡的0x004084f8《字串中的字元無法…>0x004084f8《妄…>有本質區別,後者記憶體位址對應著乙個實際的記憶體空間,記憶體中儲存的資料顯示亂碼。而前者儘管看似為乙個記憶體位址值,但並未對應到乙個實際的記憶體空間,好比乙個學號之前是可以對應乙個學生的,但是學生資訊登出之後,儘管該學號存在,但是無法查詢到此人。若理解有誤,懇請指正,虛心學習~) ,此時data成為乙個垂懸指標。

6、避免垂懸指標的存在,將指標指向null:

7、第一次析構結束:

8、第2次析構,即物件a的析構。此時可以觀察到data儲存的值已經變為0x004084f8《字串中的字元無法…>,即data是乙個野指標了。

9、繼續析構,則導致程式崩潰:

上述除錯則解釋了淺拷貝可能造成的影響即:

1、同一記憶體空間析構2次引起程式崩潰

2、乙個類成員修改資源,會使得另乙個也隨之改變

3、第3點有疑問,data指向的記憶體儲存在堆中,但是已經由物件b給釋放了,而data是乙個區域性變數,其儲存位址在棧中,程式結束,系統自動收回棧中的資源,那麼此時所謂的記憶體洩露是洩露了哪一部分記憶體資源呢???表示不太理解,或許我理解有誤???

定義:

當拷貝物件中有對其他資源(如堆、檔案、系統等)的引用時(引用可以是指標或引用)時,物件另開闢一塊新的資源,而不再對拷貝物件中資源的指標或引用進行單純的賦值。簡單地說,即是非共享同一塊記憶體資源,而是重新開闢一塊內容,將資料複製到新開闢的記憶體中。

通過乙個例項來反應:

#include

using

namespace

std;

class a

a(const a& a)

~a()

private:

char* data;

};int main(int argc, char* argv)

採用單步除錯f11:

1、程式執行到物件a的例項化:

2、進入物件a的建構函式中:

3、此時在堆中為data開闢了乙個記憶體,記憶體位址為0x005584f8

4、注意,在淺複製時,程式直接執行到return 0;。而深複製,f11後進入複製建構函式:

5、觀察此時data指向的記憶體位址為0x00558d10a.data(0x005584f8)是不同的。memcpy的目的即為複製資料。

6、執行物件b的析構,此時data的值為0x00558d10,正好是b物件複製構造中分配的記憶體空間的位址。

7、此時物件b佔據的堆中的資源被釋放。

8、第2次析構,即a物件中資源的釋放,此時data的值為0x005584f8

9、程式沒有出現崩潰的狀況,表明2次析構成功。

那麼,什麼時候用淺拷貝?什麼時候用深拷貝呢?

中認為最好使用深拷貝或智慧型指標。深拷貝相對於淺拷貝來說,會佔據額外的記憶體資源,但是使用更加安全。

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

1 函式的引數為類物件且引數採用值傳遞方式 2 將類物件做為函式的返回值。2.淺拷貝 所謂淺拷貝,指的是在物件複製時,只對物件中的資料成員進行簡單的賦值 預設拷貝建構函式執行的也是淺拷貝。大多情況下 淺拷貝 已經能很好地 工作了,但是一旦物件存在了動態成員,那麼淺拷貝就會出問題了,讓我們考慮如下一段...

拷貝建構函式,深拷貝,淺拷貝

深淺拷貝的區別 淺拷貝是將原始物件中的資料型字段拷貝到新物件中去,將引用型字段的 引用 複製到新物件中去,不把 引用的物件 複製進去,所以原始物件和新物件引用同一物件 新物件中的引用型字段發生變化會導致原始物件中的對應欄位也發生變化。line line const line obj 深拷貝是在引用方...

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

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