Copy-On-Write 技术――拖延战术的产物

Copy-On-Write

string类内存分配的概念

 通常,string类中必须有一个私有成员,其一个是char*记录从堆上分配内存的地址,其在构造时分配内存,在析构时释放内存,因为是从堆上分配内存,所以string类在维护这块内存上是十分小心的,string类在返回这块内存地址时,只返回const char*,也就是 只读,如果你要写,就只能通过string提供的方法。

内存共享&Copy-On_write

1 Copy-On-Write的原理是什么?

 Copy-On-Write使用了”引用计数“,必然有一个变量pCount,当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当其他类需要这块内存时,这个计数会自动累加,当有析构时,这个计数会减一,直到最后一个类析构时,此时的pCount为1或0,程序会真正的free这块从堆上分配的内存。

2 string类在什么情况下共享内存?

根据常理和逻辑,如果一个类需要另一个类的数据,那就可以共享被使用类的内存了。于是,就是以下两种情况时,(1)以别的类构造自己时 (2)以别的类赋值时

【注】构造与析构的差别

例1 :拷贝构造

string str1 = "hello world";
string str2 = str1;

以上语句等价于

string str1 (hello world);  //调用构造函数
string str2 (str1);         //调用拷贝构造函数

总结:并不是“=”就是赋值操作

例2: 赋值

string str2;               //调用默认空字符串的构造函数
 str2 = str1;              // 调用str2的赋值操作

以上均会发生内存共享的情况,当然也有特例。

char tmp[]="hello world";
string str1 = tmp;
string str2 = tmp;

3 类在什么情况下才发生写时才拷贝(Copy-On-Write)

string类的[]、=、+=、+、操作符,还有例如insert、replace、包括析构等成员函数时。

【注】修改数据是才会触发Copy-On-Write

4 Copy-On-Write具体实现是怎样的?

就像这样,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造或者赋值时,这个内存的值就会加1,而在内容修改时,string类查看这个引用计数是否为0,如果不为0,表示有人共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减一,再把数据拷贝下来。

①构造函数(分配内存)

string::string(const char* tmp)
{
    _len = strlen(tmp);
    _ptr = new char[_len+1+1];
    strcpy(_ptr,tmp);
    *(int*)(_ptr-4)=0;
}

②拷贝构造(共享内存)

string::string(const string& str)
{
    if(*this != str)
    {
        this->_ptr = str.c_str();
        this->_len = str.size();
        *(int*)(_ptr-4)++;
    }
}

③写时才拷贝(Copy-On-Write)

char& string::operator[](unsigned int idx )
{
    if(idx > _len || _ptr==0)
    {
        char nullchar = 0;
        return nullchar ;
    }
    (*(int*)(_ptr-4))--;
    char* tmp = new char[_len+1+1];
    strncpy(tmp,_ptr,_len+1);
    _ptr = tmp;
    (*(int*)(_ptr-4))=0;   //设置新的引用计数
    return _ptr[idx];
}

④析构函数的一些处理

~string()
{
    (*(int*)(_ptr-4))--;
    if(_ptr[_len+1]==0)
    {
        delete[] _ptr;
    }
}


你可能感兴趣的:(计数,引用计数,写时拷贝)