String类的深拷贝浅拷贝引用计数

在模拟实现String类的过程中,不可避免的会遇到深拷贝浅拷贝的问题,下面就深拷贝浅拷贝做一个简介。

浅拷贝:

也称位拷贝,编译器只是直接将指针的值拷贝过来,结果多个对象共用同一块内存,当 一个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,所以在对这段内存进行操作的时候,发生了访问违规。

深拷贝:

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。将资源和值一块拷贝过来,此时两个对象各自占用资源,尽管值相同,但是互不影响。

//浅拷贝
class String 
{
public:
    String(const char* s = "")
    {
        if (NULL == s) 
        {
            _pStr = new char[1];
            *_pStr = '\0';
        }
        else
        {
            _pStr = new char[strlen(s) + 1];
            strcpy(_pStr, s);
        }
    }
    String(const String& s)
    {
        _pStr = s._pStr;
    }
    String& operator=(const String& s)
    {
        if (this != &s) 
        {
            _pStr = s._pStr;
        }
        return *this;
    }
    ~String()
    {
        if (NULL != _pStr)
        {
            delete[] _pStr;
            _pStr = NULL;
        }
    }

private:
    char* _pStr;
};
//深拷贝--普通版本
class String 
{
public:
    String(const char* s = "")
    {
        if (NULL == s)
        {
            _pStr = new char[1];
            *_pStr = '\0';
        }
        else 
        {
            _pStr = new char[strlen(s) + 1];
            strcpy(_pStr, s);
        }
    }
    String(const String& s) : _pStr(new char[strlen(s._pStr) + 1])
    {
        strcpy(_pStr, s._pStr);
    }
    String& operator=(const String& s)
    {
        if (this != &s)
        {   
            char* temp = new char[strlen(s._pStr) + 1];
            strcpy(temp, s._pStr);
            delete[] _pStr;
            _pStr = NULL;
            _pStr = temp;
        }
        return *this;
    }
    ~String()
    {
        if (NULL != _pStr) 
        {
            delete[] _pStr;
            _pStr = NULL;
        }
    }
private:
    char* _pStr;
};

//深拷贝--简洁版
class String 
{
public:
    String(const char* s = "")
    {
        if (NULL == s) 
        {
            _pStr = new char[1];
            *_pStr = '\0';
        }
        else 
        {
            _pStr = new char[strlen(s) + 1];
            strcpy(_pStr, s);
        }
    }
    String(String& s) :_pStr(NULL) 
    {
        String temp(s._pStr);
        swap(_pStr, temp._pStr);
    }
    String& operator=(String& s)
    {
        if (this != &s) 
        {
            swap(_pStr, s._pStr);
        }
        return *this;
    }
    ~String()
    {
        if (NULL != _pStr) 
        {
            delete[] _pStr;
            _pStr = NULL;
        }
    }
private:
    char* _pStr;
};

下面谈一下String类浅拷贝存在的问题
这里写图片描述
在浅拷贝时,两个对象指向了同一块内存,对象销毁时该空间被释放了两次,程序会崩溃!

下面给出引用计数

//引用计数
class String 
{
public:
    String(const char* s = "")
        : _pCount(new int(1))
    {
        if (NULL == s) 
        {
            _pStr = new char[1];
            *_pStr = '\0';
        }
        else 
        {
            _pStr = new char[strlen(s) + 1];
            strcpy(_pStr, s);
        }
    }
    String(const String& s)
    {
        _pStr = s._pStr;
        _pCount = s._pCount;
        (*_pCount)++;
    }
    String& operator=(const String& s)
    {
        if (this != &s) 
        {
            if (--(*_pCount) == 0)
            {
                delete[] _pStr;
                delete _pCount;
            }
            _pStr = s._pStr;
            _pCount = s._pCount;
            (*_pCount)++;
        }
        return *this;
    }
    ~String()
    {
        if (NULL != _pStr && --(*_pCount) == 0)
        {
            delete[] _pStr;
            delete _pCount;
        }
        _pCount = NULL;
        _pStr = NULL;
    }

private:
    char* _pStr;
    int* _pCount;
};

用引用计数实现String类时引用计数使用静态成员变量的原因:

在引用计数引入计数虽然防止了内存被析构释放多次的可能,但是随之带来的却是计数器count的不统一,导致内存无法被释放。为了解决这个问题,所以加入了静态成员变量来保证计数器的统一,来方便后面内存空间的释放。

写时拷贝

//写时拷贝
class String
{
public:
    String(const char *pstr = "")
    {
        if (NULL == pstr)
        {
            _pstr = new char[1 + 4];
            *(_pstr + 4) = '\0';
        }
        else
        {
            _pstr = new char[strlen(pstr) + 1];
            _pstr += 4;
            strcpy(_pstr, pstr);
        }
        getRef() = 1;
    }

    String(const String &s)
        :_pstr(s._pstr)
    {
        ++getRef();
    }

    String& operator=(const String &s)
    {
        if (_pstr != s._pstr)
        {
            if (0 == --getRef())
            {
                delete[](_pstr - 4);
            }
            _pstr = s._pstr;
            ++getRef();
        }
        return *this;
    }

    char& operator[](size_t index)  
    {
        return *(_pstr + index);                                                                              //一空间,则一个改变,另一个也会改变  
    }
    const char& operator[](size_t index)const   
    {                                           
        return *(_pstr + index);
    }


    char& operator[](size_t index)
    {
        if ((getRef()>1))
        {
            --getRef();
            char* pstr = new char[strlen(_pstr) + 1 + 4];
            strcpy(pstr, _pstr);
            _pstr = pstr;
            ++getRef();
        }
        return *(_pstr + index);
    }

    ~String()
    {
        if (0 == --getRef())
        {
            delete[](_pstr - 4);
            _pstr = _pstr - 4;
            _pstr = NULL;
        }
    }
private:
    char *_pstr;
    int& getRef()
    {
        return *((int*)_pstr - 1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值