字符类->浅拷贝与深拷贝

来源:互联网 发布:长江证券开户软件下载 编辑:程序博客网 时间:2024/05/18 00:03

首先就深浅拷贝的问题做一个解释;

所谓浅拷贝,也称位拷贝,就是在类中拷贝构造函数以及赋值运算符重载时。通过直接将指针的值拷贝,与原对象共用一个空间;
深拷贝,在以所述的两种函数中重新申请一块空间存放新的对象;
系统自动生成的拷贝构造即为浅拷贝;

class String{public:    String(const char* pStr = "")    {        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)    {    }    String& operator=(const String& s)    {        if (this != &s)        {            delete[]_pStr;            _pStr = s._pStr;        }        return *this;    }    ~String()    {        delete[]_pStr;        _pStr = NULL;    }private:    char* _pStr;};

在这种浅拷贝的情况下,不难发现当析构时由于浅拷贝会对同一块空间两次释放,为了避免这种情况,我们对这种浅拷贝进行改造:带有引用计数,用指针指向引用计数的空间

private:    char* _pStr;    int* _pCount;

添加一个新的成员:一个整型指针用来存放当前指向同一空间的对象个数
首先是构造函数与拷贝构造函数:

String(const char* pStr = "")//构造        :_pCount(new int[1])    {        if (NULL == pStr)        {            _pStr = new char[1];            *_pStr = '\0';        }        else        {            _pStr = new char[strlen(pStr) + 1];            strcpy(_pStr, pStr);        }        *_pCount=1;    }    String(const String &s)//拷贝构造        :_pStr(s._pStr)        , _pCount(s._pCount)    {        (*_pCount)++;//注意优先级    }

赋值运算符重载时,需注意原对象的空间是否还有其他对象在使用,若无便释放,若有则计数减一;
这里写图片描述
代码如下:

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

最后是析构函数,检测计数是否为一,判断是否释放空间

    ~String()    {        if (0 == --(*_pCount))        {               delete[]_pStr;            delete[]_pCount;            _pStr = NULL;            _pCount = NULL;        }    }

这种使用计数指针的方法可以避免浅拷贝的出错,但多出一个指针成员终归是不理想的,参考new[]的做法,我们可以将这个计数放在所申请的空间前,只需多申请4个字节以及做一些处理;

class String{public:    String(const char* pStr = "")        :_pStr(new char[strlen(pStr) + 4+1])    {        *((int *)_pStr) = 1;        _pStr += 4;        strcpy(_pStr, pStr);    }    String(const String &s)        :_pStr(s._pStr)    {        ++(*((int *)(_pStr - 4)));    }    String& operator=(const String& s)    {        if (this != &s)        {            if (0 == --(*((int *)(_pStr - 4))))            {                _pStr -= 4;                delete[] _pStr;            }            _pStr = s._pStr;            ++(*((int *)(_pStr - 4)));        }        return *this;    }    ~String()    {        if (0 == --(*((int *)(_pStr - 4))))        {               _pStr -= 4;            delete[]_pStr;            _pStr = NULL;        }    }private:    char* _pStr;};

下面介绍深拷贝

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

与浅拷贝原始函数相比,仅拷贝构造函数与赋值运算符重载需要修改
再运用swap函数对其改造

    String(const String &s)        :_pStr(NULL)    {        String str(s._pStr);        swap(_pStr, str._pStr);    }

修改后的拷贝构造,需注意要将_pStr赋空指针,否则会造成销毁野指针而出错;

String& operator=(String s)    {        swap(_pStr, s._pStr);        return *this;    }

赋值运算符重载中,利用拷贝构造一个s,出函数时会对其进行析构,函数中利用s对this中的指针进行交换赋值。

1 0
原创粉丝点击