引用计数的写时拷贝

来源:互联网 发布:如何注册淘宝商标 编辑:程序博客网 时间:2024/05/22 01:28

什么是写时拷贝

首先我们需要知道什么是写时拷贝,写时拷贝,通俗点说也就是写的时候拷贝。那么什么是写的时候拷贝呢,这又是什么意思呢?
举个例子,创建一个日期类的对象,然后又用这个对象拷贝构造了多个堆想,也就是说这几个对象所指向的是同一块空间,那么当你对其中一个对象进行读操作的时候,什么问题都不会有,那么当你对某个对象进行写操作的时候,问题就出现了,一个对象的改变会影响其他对象,但是这并不是我们想要的,虽然说共用同一块空间,但是对象是独立的。我的改变应该不会影响别人。那么为了解决这个问题,我们就引入了写时拷贝这个概念,就是说,当你要对某个对象进行写才做,而这个对象又与其他对象共用同一块空间,此时,就需要,再重新开一段空间,吧你要进行写操作的那个对象拷贝过来,然后再进行写操作,这样就不会影响其他的对象了。
下面一段代码来解释一下

#include<iostream>using namespace std; class String   {   private:     char* _str;   public:    String(char* str = "")        :_str(new char [strlen(str)+1])    {        strcpy(_str,str);        cout<<"String(char*)"<<endl;    }    String(const String&str)        :_str(str._str)    {        cout<<"String(const &)"<<endl;    }    ~String()    {        if(_str!=NULL)        delete []_str;        _str = NULL;        cout<<"~String"<<endl;    }  }; int main(){    String s1("qwer");    String s2(s1);    String s3(s2);    return 0;}

运行结果如下图
这里写图片描述
图中我们可以看到,三个对象的地址是相同的,也就是是,指向同一块空间,但是按照常规的特点,三个对象应该是要析构三次,但是在这里,因为是只有一块空间,析构一次后,在慈溪构系统就崩溃了。为了解决这个问题我们引入了引用计数,就是可以定义一个变量用来宝存,一块空间对象的个数,当要进行写操作的时候就按照上面所说的方法,先拷贝再写,但是需要改变引用计数。这样就可以实现写时拷贝了。

写时拷贝的代码(引用计数)

class String{private:    char *_str;    int *_refCountPtr;    int _capacity;    int _size;public:    String(char *str = "")        :_refCountPtr(new int(1))    {        _capacity = strlen(str);        _size = _capacity;        _str = new char[_capacity+1];        strcpy(_str,str);        cout<<"String(char *str = "")"<<endl;    }    String(String&s)        :_str(s._str)        ,_capacity(0)        ,_size(0)    {        _refCountPtr = s._refCountPtr;//改变引用计数        (*_refCountPtr)++;        cout<<"String(String&s)"<<endl;    }    String &operator = (String&s)    {        cout<<"operator="<<endl;        if(_str!=s._str)//自己给自己赋值        {            if((*_refCountPtr)==1)            {                delete[]_str;                delete _refCountPtr;            }            _str = s._str;            _refCountPtr = s._refCountPtr;            (*_refCountPtr)++;        }        return *this;    }    ~String()    {        Release();        cout<<"~String()"<<endl;    }};

还有另外一种方法来实现写时拷贝,具体思想就是,在构造对象的时候就为多开辟四个字节用来存医用技术,这样就不需要变量了,要用引用计数的的时候只需要把它取出来就可以了。

写时拷贝(指针)

class String{private:    char* _pStr;public:    String(const char* pStr = "")    {        if(NULL == pStr)//传了一个空字符串        {            _pStr = new char[1 + 4];//用了一个指针,多四个字节            _pStr = (char*)(((int*)_pStr)+1);//向后走四个字节            *_pStr = '\0';        }        else        {            _pStr = new char[strlen(pStr) + 1 + 4];            _pStr = (char*)(((int*)_pStr)+1);            strcpy(_pStr, pStr);        }        *((int*)_pStr - 1) = 1;//引用计数初始化为1    }    String(const String&s)//直接拷贝,让引用计数加加就好,后面的事析构会做        :_pStr(s._pStr)    {        ++GetCount();    }    String& operator=(const String& s)    {        if(this != &s)//判断是否自己给自己赋值        {            Release();//因为这里要析构,一般我们不会显示的调用析构,所以封装一个函数来代替            _pStr = s._pStr;            ++GetCount();        }        return *this;    }    ~String()    {        Release();    }private:    int& GetCount()const    {        return *((int*)_pStr - 1);//把引用计数取出来    }    void Release()    {        if(_pStr && (0 ==GetCount()--))        {            _pStr = (char*)((int*)_pStr-1);            delete[] _pStr;            _pStr = NULL;        }    }};

上面这两种方法都可以实现写时拷贝。

原创粉丝点击