【C++】深拷贝、浅拷贝和写时拷贝

时间:2020-12-20 19:56:00

大家首先来看一下下面这段代码:

void Test()
{
    int *p1 = new int[10];
    int *p2 = p1;
    delete[] p1;
    delete[] p2;
}

运行这段代码:
【C++】深拷贝、浅拷贝和写时拷贝
我们发现这段程序无法运行,经过测试我们发现,p1和p2指向同一块空间:
【C++】深拷贝、浅拷贝和写时拷贝
这就是我们所说的浅拷贝。
浅拷贝:也称位拷贝,编译器只是直接将指针的值拷贝过来,结果多个对象共用同一块内存,当一个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,所以在对这段内存进行操作的时候,发生了访问违规。
【C++】深拷贝、浅拷贝和写时拷贝
【深拷贝】
【C++】深拷贝、浅拷贝和写时拷贝
构造s2时拷贝一块跟s1指向数据块一样大的数据块,并将值拷贝下来,这样s1和s2指向各自的数据块,析构时释放各自的数据块。
String类实现深拷贝
普通写法:

String(const String& s)
{
    _str = new char[strlen(s.str)+1];
    strcpy(_str,s._str);
}

现代写法:

String(const String& s)
      :_str(NULL)
{
    String tmp(s._str);
    swap(_str,tmp._str);
}

【写时拷贝】
【C++】深拷贝、浅拷贝和写时拷贝

class String {
  privatechar *_str;
  static int refCount;
}
int String::refCount=0;

如此定义一个静态变量,会存在下面一个问题:
【C++】深拷贝、浅拷贝和写时拷贝
我们可以采用下面的方式进行定义:

class String
{
public:
String(const char* str=" ")          //构造函数
      :_str(new char [strlen(str)+1])
      ,_refCount(new int(1))
{
    strcpy(_str,str);
}
String(String & s)             //拷贝构造
      :_str(s._str)
      ,_refCount(s._refCount)
{
      (*_refCount)++;
}
~String()                     //析构函数
{
    if(--(*refCount)==0)
    {
       delete[]  _str;
       delete _refCount;
     }
}
private:
   char *_str;
   int *_refCount;
};

赋值运算符的重载我们需要考虑以下几种情况:
【C++】深拷贝、浅拷贝和写时拷贝

String& operator = (const String& s)
{
      if(_str != s._str)
      {
           if(--(*_refCount)==0)
          {
            delete[] _str;
            delete _refCount;
          }
          _str = s._str;
          _refCount = s._refCount;
          (*refCount)++;
      }
      return *this;
}

写时拷贝

void CopyWrite()
{
     if(*refCount>1)
     {
        char* newStr = new char[strlen(_str)];
        strcpy(newStr,_str);
        (*_refCount)--;
        _str = newStr;
        _refCount = new int(1);
     }
}

第二种实现方式:
【C++】深拷贝、浅拷贝和写时拷贝

String(const char* str = " ")
      :_str(new char[strlen(str)+5]
{
     strcpy(_str+4,str);
     *((int*)_str) = 1;
     _str += 4;
}

String(const String & s)
      :_str(s._str)
{
     (*(int*)(_str-4))++;
}
~String()
{
     if(--*((int*)(_str-4))==0)
     {
     delete[](_str-4);
     }
}
String & operator = (const String & s)
{
   if(_str != s.s_str)
   {
   if(--GetRefCount() == 0)
   {
      delete[] (_str-4);
   }
   _str = s.s_str;
   GetRefCount()++;
   }
   return *this;
}
int& GetRefCount()
{
    return (*(int *)(_str-4));
}