String类之引用计数和写时拷贝

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39412582/article/details/81675238

string 类写到这里已经是升级版了,大家能看懂多少算多少,在面试的时候尽量不要给面试官说你懂这个,万一把自己搞糊涂了,岂不是得不偿失。
好了 ,废话到此结束,正文开始。

引用计数

我们知道,在浅拷贝当中,由于多个对象共用同一块空间,最后释放内存的时候导致同一块内存多次释放而出现问题,那么问题来了,能否保证当多个对象共同使用同一块空间时,该空间只释放一次?
答案当然是可以的了。
引用计数原理:当多个对象共享同一块资源时,要保证该资源只释放一次,只需要记录有多少个对象在管理这份资源即可,每增加(减少)一个对象相时,给该记数加一(减一),当最后一个对象不使用时,该对象负责将资源释放掉即可。
具体操作我们看代码:

class String
{
public:
    String(const char* pStr = "")
        :_pCount(new char(1))
    {
        if (NULL == pStr)
        {
            _pStr = new char[1];
            *_pStr = '\0';
        }
        else
        {
            _pStr = new char[strlen(pStr) + 1];
            strcpy(_pStr, pStr);
        }
    }

    String(const String& s)
        :_pStr(s._pStr)
        , _pCount(s._pCount)  //引用计数
    {
        ++(*_pCount);   //有几个对象调用拷贝构造函数就给_pCount++一下,计算具体有几个对象在共同使用这块空间
    }
    String& operator=(const String& s)
    {
        if (_pStr != s._pStr)
        {
            if (_pStr && 0 == (--(*_pCount)))  //如果这块空间存在并且只有一个对象在管理这块空间就进行下一步
            {
                delete[] _pStr;
                delete _pCount;
            }
            _pStr = s._pStr;
            _pCount = s._pCount;
            ++(*_pCount);
        }
        return *this;
    }
    ~String()
    {
        if (_pStr && 0 == (--(*_pCount)))
        {
            delete[] _pStr;
            _pStr = NULL;

            delete _pCount;
            _pCount = NULL;
        }
    }
private:
    char* _pStr;
}

采用引用计数后虽然解决了这个问题,但它仍然是浅拷贝,而且,如果对象很多的话,我们可能会忘记释放某一些空间,为了完美的解决这个小小的缺陷,我们又引进了写时拷贝这个概念。

以前在动态内存开辟中说new[]的时候应该说过new[]在底层其实人家是给它多开辟了4个字节的内存,用来存放引用记数,这样不仅解决了上述问题还可以让我们随意更改单个字符。

public:
    String(const char* pStr = "")
    {
        if (NULL == pStr)
        {
            _pStr = new char[1+4];
            _pStr += 4;
            *_pStr = '\0';
        }
        else
        {
            _pStr = new char[strlen(pStr) + 1+4];
            _pStr += 4;
            strcpy(_pStr, pStr);
        }
        GetRef() = 1;    //放引用计数
    }

    String(const String& s)
        :_pStr(s._pStr)
    {
        GetRef()++;
    }
    //写时拷贝----COW(Copy On Write)只能用于单线程
    String& operator=(const String& s)
    {
        if (_pStr != s._pStr)
        {
            Release();

            _pStr = s._pStr;
            ++GetRef();
        }
        return *this;
    }
    ~String()
    {
        Release();
    }
    char& operator[](size_t index)
    {
        if (GetRef() > 1)
        {
            char* pTemp = new char[strlen(_pStr) + 1 + 4];
            *(int*)pTemp = 1;  //放引用计数
            pTemp += 4;
            strcpy(pTemp , _pStr);
            --GetRef();
            _pStr = pTemp;
        }
        return _pStr[index];
    }
    const char& operator[](size_t index)const
    {
        return _pStr[index];
    }
private:
    int& GetRef()    //引用计数
    {
        return *((int*)_pStr - 1);
    }
    void Release()   //释放空间
    {
        if (_pStr && 0 == --GetRef())
        {
            _pStr -= 4;
            delete[] _pStr;
            _pStr = NULL;
        }
    }
private:
    char* _pStr;
};

void TestFunc()
{
    String s1("Hello");
    String s2(s1);
    s2[0] = 'w';
    String s3;
    s3 = s2;
    const String s4("Bit");
    s4[1];
}

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

看看效果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_39412582/article/details/81675238
今日推荐