您好,登錄后才能下訂單哦!
小編給大家分享一下C++中淺拷貝、深拷貝、寫時拷貝的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
對于普通的類型來說,拷貝沒什么大不了的。
int a = 0;int b = a;
不會出現任何問題。
而類對象與普通對象不同,類對象內部結構一般較為復雜,存在各種成員變量。
首先來說說我們常遇到的淺拷貝的情況。
#include <stdio.h> class student {public: student() // 構造函數,p指向堆中分配的一空間 { _name = new char(100); printf("默認構造函數\n"); } ~student() // 析構函數,釋放動態分配的空間 { if (_name != NULL) { delete _name; _name = NULL; printf("析構函數\n"); } }private: char * _name; // 一指針成員};int main() { student a; student b(a); // 復制對象 return 0; }
這段代碼乍看之下沒什么毛病,通過類的默認構造函數將 a 復制給 b ,但是一旦運行就會程序崩潰。
經過我的刻苦學習與鉆研,終于發現其中的問題所在。
由于我的類沒有拷貝構造函數,所以student b(a)
會調用,編譯器自動生成的一個默認拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝。
淺拷貝:
淺拷貝只是拷貝了指針,并沒有創建新的空間,使得兩個指針指向同一個地址,這樣在對象塊結束,調用函數析構的時,會造成同一份資源析構2次,即delete同一塊內存2次,造成程序崩潰。
淺拷貝使得 a 和 b 指向同一塊內存,任何一方的變動都會影響到另一方。
由于 a 和 b 指向的是同一塊內存空間,當 a 釋放了后,b 指向的內存空間不復存在,所以會出現內存泄露的情況。
如何避免淺拷貝害人呢?
養成自定義拷貝構造函數的習慣,當顯式定義了拷貝構造函數后,編譯器就會調用拷貝構造函數了,為了不出現程序崩潰,請使用自定義拷貝構造函數,當然我們自己如果把代碼寫成了淺拷貝的形式,那也不是不可能的事。
// 使用自定制拷貝構造函數,完成深拷貝!!!class A {public: A() // 構造函數,p指向堆中分配的一空間 { m_pdata = new char(100); printf("默認構造函數\n"); } A(const A& r) // 拷貝構造函數 { m_pdata = new char(100); // 為新對象重新動態分配空間 memcpy(m_pdata, r.m_pdata, strlen(r.m_pdata)); printf("copy構造函數\n"); } ~A() // 析構函數,釋放動態分配的空間 { if (m_pdata != NULL) { delete m_pdata; printf("析構函數\n"); } }private: char *m_pdata; // 一指針成員};int main() { A a; A b(a); // 復制對象 return 0; }
在拷貝構造函數中,為 b 對象 new 了一個新的空間,這樣 a 和 b 指向的是不同的空間,只是內容一致,但是互不影響。
重復的去開辟空間和釋放空間效率是很低的,聰明的地球人決定使用寫時拷貝。
寫時拷貝:引入一個計數器,每片不同內容的空間上都再由一個計數器組成,在構造第一個類指向時,計數器初始化為1,之后每次有新的類也指向同一片空間時,計數器加 1 ;在析構時判斷該片空間對應計數器是否為1,為1則執行清理工作,大于1則計數器減 1 。如果有需要進行增刪等操作時,再拷貝空間完成,有利于提高效率。
class String { public: String(const char* str = "") :_str(new char[strlen(str) + 1 + 4])//+1表示字符串后面要放一個'\0',+4表示多開辟一個空間存放引用計數 { _str += 4;//_str指向數據存放區 strcpy(_str, str); _GetCount() = 1; } String(const String& s) :_str(s._str) { _GetCount()++; } String& operator=(String& s) { if (this != &s) { if (--_GetCount() == 0) { delete[](_str - 4); } ++s._GetCount(); _str = s._str; } return *this; } ~String() { if (--_GetCount() == 0) { delete[](_str - 4); // 注意:由于計數器存放在了_str首地址-4的地址上,所以在析構時一定要注意全部釋放,避免內存泄漏。 } } public: int& _GetCount() { return *((int*)_str - 1); } private: char* _str; };
以上是“C++中淺拷貝、深拷貝、寫時拷貝的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。