深淺拷貝 & 類型萃取

一、深淺拷貝問題

             1)什麼是淺拷貝
所謂的淺拷貝是指在源對象向目標對象進行拷貝的時候 將源對象的各個成員逐一賦給新對象的成員
這樣做看似無可厚非,實際上如果源對象的數據成員中有指向一片內存空間的指針變量是 這種淺拷貝只是將這個指針變量作爲右值付給目的對象的指針。

這樣做的結果是新對象的指針和源對象的指針指向同一片內存空間 。
改動數據時在用戶的角度上看似改動了拷貝後的目的對象,實際上也將源對象的數據改動了。
主程序結束時,對象被逐個撤銷,先撤銷對象st2(因爲st2後創建),撤銷前先調用析構函數,用delete運算符釋放所分配的內存空間;撤銷對象st1時,第二次調用析構函數,因爲這時_name所指向的內存空間已經被釋放,企圖對同一塊內存空間釋放兩此,所以這時候程序出錯。
              如圖所示:
            
                                   
注意:C++中類的默認構造函數 和庫中一些實現數據拷貝的庫函數都是淺拷貝的

2)所謂深拷貝就是爲新的對象指針開闢一段新空間 在這段空間內賦源對象指針空間內的數據內容 這樣目的對象和源對象有兩個類容一樣的空間。
更改和釋放時都對自己管理的空間進行操作 
如果要解決淺拷貝問題就必須實現深拷貝
1.類中自定義的拷貝構造函數和賦值運算符的重載
   兩種寫法:
基本寫法:
基本寫法的思路是 爲目的對象的成員指針開闢合適的空間  之後將源對象的指針賦給新對象將源對象指針指向空間的數據內容賦給新對象指針空間
          String(constString& s)
          {
              _str =newchar[strlen(s._str) + 1];
              strcpy(_str, s._str);
          }
          String&operator=(constString& s)
          {
              if(this!= &s)
              {
                   char* tmp = new char[strlen(s._str) + 1];
                   strcpy(tmp, s._str);
                   delete[] _str;
                   _str = tmp;
              }
              return*this;//爲了支持鏈式訪問
          }
如圖:
圖中紅色箭頭代表拷貝
現代寫法:
現代寫法的基本思路是 利用寫好的構造函數在烤貝構造函數和賦值運算符重載內部構造出一個臨時對象
之後將目的對象的成員變量與臨時對象的成員變量交換 這是目的對象中指針的指向是臨時對象中指針指向的
用構造函數創建的空間 該空間中數據內容和源對象一樣是兩份。
拷貝構造函數結束後臨時對象調用析構函數釋放了原目的對象的對象空間,原本的臨時對象作爲目的對象被保留了下來.


代碼:
          String(char* str = "")
              :_str(newchar[strlen(str) + 1])
          {
              strcpy(_str, str);
          }
          String(constString& s)
              :_str(NULL)
          {
              Stringtmp(s._str);
              swap(_str, tmp._str);
          }
          String&operator= (constString& s)
          {
              if(this!= &s){
                   Stringtmp(s._str);
                   swap(_str, tmp._str);
              }
              return*this;
          }

什麼是類型萃取:
現在先定義一個函數:
//template<class T>
//void Copy(T* dst, T*src,size_t size)
//{
//   memcpy(dst, src, sizeof(T)*size);
//}
該函數調用memcpy()該函數將源地址中的數據按規定的字節以二進制形式拷貝到新空間去
首先在進行拷貝的是Sring類的對象數組,而memcpy函數的功能是從源src所指的內存地址的起始位置開始拷貝n個字節到目標dest所指的內存地址的起始位置中。
此時s1,s2指向的是同一塊內存,並且s2原有的內存丟失了,析構的時候先將s2析構掉,再將s1析構掉,此時同一塊內存會被析構多次,則程序一定會崩潰。
此時我們不能選擇用memcpy進行拷貝,選擇使用for循環

內置類型我們可以通過memcopy和memmove這兩個方式進行拷貝,自定義類型或string我們要通過賦值的方式拷貝。這樣的話會提高效率。

類型萃取的方式:類型萃取是在模板的基礎上區分內置類型和其他類型,主要原理是將內置類型全部特化,然後再進行區分。


#include<iostream>
#include<stdlib.h>
#include<string>
usingnamespacestd;
//
//template<class T>
//void Copy(T* dst, T* src,size_t size)
//{
//   memcpy(dst, src, sizeof(T)*size);
//}
struct_TrueType
{
     staticboolGet()
     {
          returntrue;
     }
};
struct_FalseType
{
     staticboolGet()
     {
          returnfalse;
     }
};
template<classT>
structTypeTraits
{
     typedef_FalseType_IsPod;
};
template<>
structTypeTraits<int>
{
     typedef_TrueType_IsPod;
};
template<>
structTypeTraits<char>
{
     typedef_TrueType_IsPod;
};
template<classT>
voidCopy(T* dst, T* src,size_tsize)
{
     if(TypeTraits<T>::_IsPod().Get())//構建一個臨時對象
          memcpy(dst, src,sizeof(T)*size);
     else{
          for(size_ti = 0; i < size; ++i){
              dst[i] = src[i];
          }
     }
}
intmain()
{
     
     stringa[4] = { "111","222","333"};
     stringb[4] = { "444","555","666"};
   intdata = 9;
     int_data = 8;
     //Copy(a, b, 4);
     Copy<string> (a, b, sizeof(a) /sizeof(a[0]));
     Copy<int>(&data, &_data, 1);
     system("pause");
     return0;
}


發佈了47 篇原創文章 · 獲贊 21 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章