String —— 写时拷贝技术(Copy-On-Write)

String —— 写时拷贝技术(Copy-On-Write)


1.往磁盘里写东西时,其实是先写到内存,只有当我们关闭文件或刷新时,才会真正写到磁盘。
2.把某个对象的值赋值给另一个变量时,这个变量默认指向与原来对象相同的空间,只有真正修改其当中的内容时,才会对其分配空间。

int main()
{
    string s1("hello");
    string s2 = s1;
    printf("s1 address :%p\n", s1.c_str());
    printf("s2 address :%p\n", s2.c_str());
    s1[0] = 'H';
    s2[0] = 'W';
    printf("Copy-On-Write :\n");
    printf("s1 address :%p\n", s1.c_str());
    printf("s2 address :%p\n", s2.c_str());
    return 0;
}

从上面的代码中我们可以看出string的写时拷贝的机制,只有当写时才会给其分配空间。就是下面这种样子。

写时拷贝技术是通过"引用计数"实现的,有两种方法:
  • 一种是除了数组外,再重新开辟4个字节的空间(pCount),用来记录有多少个指针指向这块空间。
  • 另一种是,在数组的头上多分配4个字节的,用来记录有多少个指针指向块空间。
       当有新的指针指向这块空间时,引用计数+1,当要释放这块空间时,引用计数-1(假装释放),直到引用计数减为0时,才会真的释放掉这块空间。当指向的指针要改变这块空间的值时,就会为这个指针分配自己的空间,这时引用计数会发生变化,旧的空间的引用计数-1,新分配的空间引用计数+1。

第一种方法:
//_str //_pCount
class String
{
public:
     String(const char* str)
          :_str(new char[strlen(str) + 1])
          ,_pCount(new int(1))
     {
          strcpy(_str, str);
     }


     //String s2(s1)
     String(const String& s) //拷贝构造
          :_str(s._str)
          , _pCount(s._pCount)
     {
          (*_pCount)++;
     }


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


     ~String()
     {
          if (--(*_pCount) == 0)
          {
              delete[] _str;
              delete _pCount;
          }
     }


     void CopyOnWrite()
     {
          if (*_pCount > 1)
          {
              char* newstr = new char[strlen(_str) + 1];
              strcpy(newstr, _str);
              --(*_pCount);
              _str = newstr;
              _pCount = new int(1);
          }
     }


     char& operator[](size_t pos)
     {
          CopyOnWrite();
          return _str[pos];
     }


     const char* c_str()
     {
          return _str;
     }
private:
     char* _str;
     int * _pCount;
};
第二种方法:

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


     //s2(s1)
     String(const String& s)
          :_str(s._str)
     {
          GetRefCount()++;
     }


     //s1 = s2
     String& operator=(const String& s)
     {
          if (_str != s._str)
          {
              if (--GetRefCount() == 0)
              {
                   delete[](_str - 4);
              }
              _str = s._str;
              GetRefCount()++;
          }
          return *this;
     }


     void CopyOnWrite()
     {
          if (GetRefCount() > 1)
          {
              --GetRefCount();
              char* tmp = new char[strlen(_str) + 5];
              tmp += 4;
              strcpy(tmp, _str);
              _str = tmp;
              GetRefCount() = 1; 
          }
     }


     char& operator[](size_t pos)
     {
          CopyOnWrite();
          return _str[pos];
     }


     char& operator[](size_t pos) const
     {
          return _str[pos];
     }

     int& GetRefCount()
     {
          return *((int*)(_str - 4));
     }


     ~String()
     {
          if (--GetRefCount() == 0)
          {
              delete[](_str - 4);
          }    
     }
private:
     char* _str;
};

猜你喜欢

转载自blog.csdn.net/ling_hun_pang_zi/article/details/80960852