写时拷贝的两种方案

来源:互联网 发布:知乎日本海外球员名单 编辑:程序博客网 时间:2024/06/10 11:35

1. 什么是写时拷贝

写时拷贝故名思意:是在写的时候(即改变字符串的时候)才会真正的开辟空间拷贝(深拷贝),如果只是对数据的读时,只会对数据进行浅拷贝。写时拷贝:引用计数器的浅拷贝,又称延时拷贝:写时拷贝技术是通过"引用计数"实现的,在分配空间的时候多分配4个字节,用来记录有多少个指针指向块空间,当有新的指针指向这块空间时,引用计数加一,当要释放这块空间时,引用计数减一(假装释放),直到引用计数减为0时才真的释放掉这块空间。当有的指针要改变这块空间的值时,再为这个指针分配自己的空间(注意这时引用计数的变化,旧的空间的引用计数减一,新分配的空间引用计数加一)。

2. string中的两种写时拷贝

一:

  1. 动态开辟两个空间一个用来存放字符串:_str,一个用来存放计数器_refCountPtr
    这里写图片描述
  2. 每次拷贝构造时(赋值运算符的重载后半段),直接把字符串的指针付给新的String,然后给计数器加加(*_refCountPtr)++
    这里写图片描述
  3. 在释放String的时候,先对计数器减减,再判断计数器是否为零(即看是否还有指针共享此内存),若计数器为零则直接释放_str 和 _refCountPtr
  4. 在对字符串进行更改的时候(写时),就要进行深拷贝:先进行步骤3,在进行深拷贝和字符串的更改
class String//写时拷贝    {    public:        String(char * str = "\0")            :_refCountPtr(new int(1))//开辟计数器动态内存            ,_size(strlen(str))        {            _capacity = _size;            _str = new char[_capacity+1];//开辟字符串动态内存            strcpy(_str,str);           }        String(String& s)//拷贝构造        :_str(s._str)//直接浅拷贝        ,_refCountPtr(s._refCountPtr)        ,_size(s._size)        ,_capacity(s._capacity)        {            (*_refCountPtr)++;//但对计数器 ++        }        ~String()//析构        {            Release();        }        inline void Release()        {            if(--(*_refCountPtr) == 0)//计数器--,并判断是否为0            {                cout<<"~String"<<_str<<endl;                delete[] _str;                delete _refCountPtr;//释放                }        }        String &operator=(String &s)//重载        {            if(_str != s._str)//判断是否为自己给自己赋值            {                Release();//判断并处理this                _str = s._str;                _refCountPtr = s._refCountPtr;                _size = s._size;                _capacity = s._capacity;                (*_refCountPtr)++;            }            return *this;        }        char *c_str() const        {            return _str;        }        //写时拷贝        String &push_back(char ch)        {            char* str = new char[_capacity*2];            strcpy(str,_str);            //先将原来的数据保存,用于给后面重新开辟的空间赋值            Release();//处理原来的空间            _str = str;            _str[_size++] = ch;            _str[_size] = '\0';            _capacity *= 2;            _refCountPtr = new int(1);//            return *this;        }    private:        char *_str;        int *_refCountPtr;        size_t _size;        size_t _capacity;    };

二:

    开辟一个空间,前面4个字节为计数器count,剩下的为字符串_str的空间,用法与分开相同。

这里写图片描述

class String    {    public:        String(char *str = "\0")            :_capacity(strlen(str))        {            _size = _capacity;            _str = new char[_capacity + 5];//多开辟了4个字节            *(int *)_str = 1//前4个自己存放计数器            _str += 4;          //使_str指向开辟的4个字节后的空间            strcpy(_str,str);//拷贝字符串        }        String(String &s)//拷贝构造            :_str(s._str)//直接浅拷贝            ,_capacity(s._capacity)            ,_size(s._size)        {            ++(*(int *)(_str-4));//(str前的4个字节为计数器) ++        }        ~String()//析构        {            Release();        }           void Release()        {            if(--(*(int *)(_str-4)) == 0)//计数器-1判断是否为零            {                cout<<"~String "<<_str<<endl;                delete[] (_str-4);            }        }        String& operator=(String &s)//赋值重载        {            if(_str != s._str)//判断是否为自己为自己赋值            {                Release();//处理原指针(释放空间,或者计数器-1)                _str = s._str;                _size = s._size;                _capacity = s._capacity;//赋值                ++(*(int *)(_str-4));//改变先指针的计数器            }            return *this;        }        String &operator+=(char ch)//重载,会改变,会深拷贝        {            char* str = new char[_capacity*2 + 6];            strcpy(str+4, _str);//先保存原字符串            Release();//因为要重新开辟空间,所以需要处理以前的空间            *(int *)str = 1;//新开辟的计数器置1            _str = str + 4;            _str[_size++] = ch;            _str[_size] = '\0';            _capacity *= 2;            return *this;        }        String &append(const char *str)//追加字符串        {            size_t len = strlen(str);//方法同+=的重载            _capacity = len + _size;            char *tmp = new char[_capacity + 6];            strcpy(tmp+4 ,_str);            Release();            *(int *)tmp = 1;            _str = tmp + 4;            strcpy(_str+_size, str);            _size = _capacity;            return *this;        }    private:        char* _str;//开辟多4个,指向4个字节后的地址        size_t _size;        size_t _capacity;    };
原创粉丝点击