String引用计数写时拷贝

为什么要采用引用计数和写时拷贝?

在一些情况下,我们可能只会对一个对象进行读操作,如果一味的遇见一个对象,就开辟空间,并且赋值,这种操作没有任何意义,并且耗费时间和计算机的内存资源,设置一个引用计数,表示当前这段空间被几个对象引用,以自己写的String为例,当我们在使用拷贝构造或者赋值时,可以直接给引用计数加1,并且让这个新创建的对象指向这个已存在的对象的字符串,当指字符串的指针减少时(每减少1个),引用计数就减1,引用计数为0时,这段空间被回收

使用引用计数,虽然提高了效率,但是在对String进行修改时(插入、删除等)带来了麻烦,这时就需要写时拷贝,给将要修改字符串内容的指针重新开辟空间,空间内填入与原来一样的字符串,引用计数为1,原来的引用计数减一,拷贝完成之后就可对字符串进行修改

总而言之,写时拷贝就是改变了创建对象时申请空间和拷贝数据的时机,并且避免了不必要的空间申请和拷贝

下面介绍两种引用计数的方式:

第一种:

创建第一个对象时:

使用拷贝构造创建第二个对象

进行写时拷贝

代码如下:

#include <iostream>
#include <string.h>

class String 
{ 
public: 
    String(const char* str) {
        size_t len = strlen(str);
        _str = new char[len + 1];
        strncpy(_str, str,len);
        _pCount = new size_t(1);
        *(_str+len+1) = 0;
        //std::cout<<_str<<"-------"<<*_pCount<<std::endl;
    }

    //s2(s1) 
    String(const String& s) {
        *this = s;
    }

    //s2 = s1 
    String& operator=(const String& s) {
        _str = s._str;
        _pCount = s._pCount;
        ++*_pCount;
        //std::cout<<_str<<"-------"<<*_pCount<<std::endl;
        return *this;
    }
    
    ~String() {
        if(*_pCount == 1) {
            //std::cout<<_str<<"-------"<<"0"<<std::endl;
            delete[] _str;
            delete _pCount;
            _str = NULL;
            _pCount = NULL;
        } else {
            --*_pCount;
            //std::cout<<_str<<"-------"<<*_pCount<<std::endl;
        }
    }

    const char* c_str() {
        return _str;
    }

    void CopyOnWrite() {
        if(*_pCount > 1) {
            char *tmp_str = new char[strlen(_str) + 1];
            memcpy(tmp_str, _str, strlen(_str) + 1);
            size_t* tmp_pCount = new size_t(1);
            _str = tmp_str;
            --*_pCount; 
            _pCount = tmp_pCount;
        }
    }

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

private: 
    char* _str; 
    size_t* _pCount; 
}; 

void StringTest() {
    const char* str = "12345";
    String s1(str);
    String s2(s1);
    s1.CopyOnWrite();
    String s3 = s1;
    
}

int main()
{
    StringTest();

    return 0;
}

第二种:

创建第一个对象时:

使用拷贝构造创建第二个对象

进行写时拷贝

代码如下:

#include <iostream>
#include <string.h>
#include <stdio.h>
class String 
{ 
public: 
    String(const char* str) {
        _str = new char[strlen(str) + 2];
        _str[0] = 1;
        memcpy(_str+1, str, strlen(str) + 1);
        printf("%d-------%s\n", _str[0], _str+1);
    }

    //s2(s1) 
    String(const String& s) {
        *this = s;
    }

    //s2 = s1 
    String& operator=(const String& s) {
        _str = s._str;
        ++_str[0];
        printf("%d-------%s\n", _str[0], _str+1);
        return *this;
    }

    ~String() {
        if(_str[0] == 1) {
            printf("0-------%s\n",  _str+1);
            delete[] _str;
            _str = NULL;
        } else {
            --_str[0];
            printf("%d-------%s\n", _str[0], _str+1);
        }

    } 

    const char* c_str() {
        return _str+1;
    }

    void CopyOnWrite() {
        if(_str[0] > 1) {
            char* tmp = new char[strlen(_str) + 1];
            memcpy(tmp, _str, strlen(_str) + 1);
            --_str[0];
            tmp[0] = 1;
            _str = tmp;
        }
    }

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

private: 
    char* _str; // 引用计数在头上 
};

void StringTest() {
    String s1("12345");
    String s2(s1);
    s1.CopyOnWrite();
    String s3 = s1;
}


int main() {
    StringTest();
    return 0;
}

两种方式指是引用计数的位置不同,其功能都完全相同的

猜你喜欢

转载自blog.csdn.net/luhaowei0066/article/details/81282261
今日推荐