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

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

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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计并实现一个动态整型数组Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。 设计并实现一个动态整型数组Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值