写时拷贝的两种方案

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;
    };
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章