C++ 深拷貝和淺拷貝(位拷貝)詳解

前提 

在對象拷貝過程中,如果沒有自定義拷貝構造函數,系統會提供一個缺省的拷貝構造函數,缺省的拷貝構造函數對於基本類型的成員變量,按字節複製,對於類類型成員變量,調用其相應類型的拷貝構造函數。

閱讀《高質量的c c++編程》,第9章有這樣一段話,類似的話在《c++primer》《effective C++》都有所提及,那就是拷貝構造函數問題,這個是類編寫者的一個基礎問題。

位拷貝(淺拷貝)舉例,a指向b,b的改變其實會影響a的改變,同時a原本指向的空間發生泄漏。

然後這種情況下有了深拷貝。

我對其繪製思維導圖,方便閱讀並用分點的方式進行總結;

 

何時調用?

以下情況都會調用拷貝構造函數:
一個對象以值傳遞的方式傳入函數體
一個對象以值傳遞的方式從函數返回
一個對象需要通過另外一個對象進行初始化。

 

淺拷貝:位拷貝,拷貝構造函數,賦值重載

多個對象共用同一塊資源,同一塊資源釋放多次,崩潰或者內存泄漏

 

深拷貝:每個對象共同擁有自己的資源,必須顯式提供拷貝構造函數和賦值運算符。

缺省拷貝構造函數在拷貝過程中是按字節複製的,對於指針型成員變量只複製指針本身,而不復制指針所指向的目標--淺拷貝。

我們用自己編寫的string舉例

  1. class String
  2. {
  3. public:
  4. const char* c_str()
  5. {
  6. return _str;
  7. }
  8. String(const char* str = "")
  9. :_str(new char[strlen(str) + 1])
  10. {
  11. strcpy(_str, str);
  12. }
  13. String(const String &s)
  14. :_str(NULL)
  15. {
  16. String tmp(s._str);
  17. swap(_str, tmp._str);
  18. }
  19. ~String()
  20. {
  21. if (_str)
  22. {
  23. delete[]_str;
  24. }
  25. }
  26. private:
  27. char* _str;
  28. };

通過開闢空間的方式,進行深拷貝

  1. String s1("字符串1");
  2. String s2(s1);
  3. cout << s2.c_str() << endl;

拷貝成功;

這種方式採取的  拷貝構造,注意這個

  1. String(const String &s)
  2. :_str(NULL)
  3. {
  4. String tmp(s._str);
  5. swap(_str, tmp._str);
  6. }

代碼解析:其中this指向拷貝的對象,s指向試圖拷貝的原對象。(測試中的  this指向s2,s指向s1)

其中利用構造函數開闢空間,建立臨時的tmp,然後進行交換完成拷貝。

當然,我們也可以使用賦值操作符重載完成這一功能(如例子s1=s2)

  1. String& operator =(const String& s)
  2. {
  3. if (this != &s)
  4. {
  5. String tmp(s._str);
  6. swap(tmp._str, _str);
  7. return *this;
  8. }
  9. }//調用構造析構
  1. //本代碼是tmp調用的構造函數
  2. String(const char* str = "")
  3. :_str(new char[strlen(str) + 1])
  4. {
  5. strcpy(_str, str);
  6. }
  7. /*String tmp(s._str)
  8. 調用這個構造函數,開闢空間,建立一個和s1一樣大小的空間,並拷貝值
  9. */

 

代碼解析:

s1(this),s2(s)

建立tmp,tmp有和s2一樣大的空間,一樣的數值(調用構造函數),然後交換使s1(this)指向2號空間,獲得拷貝,tmp指向3號空間,tmp生命週期結束調用析構函數釋放,功能完成。

 

 

當然 賦值重載函數可以寫的更加簡潔

  1. String &operator=(String s)
  2. {
  3. swap(_str, s._str);
  4. return *this;
  5. }

利用tmp的方式是 藉助構造函數,這一種方式則是藉助拷貝構造函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章