浅拷贝、深拷贝与写时拷贝

浅拷贝、深拷贝与写时拷贝

浅拷贝

  • 在默认拷贝构造函数中,拷贝的策略是逐个成员依次拷贝。
  • 如果拷贝构造函数简单地制作了一个该对象的拷贝,而不对它的本身进行资源分配和复制,就得面临一个麻烦的局面。
  • 即,两个对象都拥有同一个资源。
  • 当对象析构时,该资源将经历两次资源返还。

    这里写图片描述

  • 创建p2时,对象p1被复制给了p2,但资源并未复制,因此,p1和p2指向同一个资源。
  • 这便被称为,浅拷贝

深拷贝

  • 当一个对象创建时,分配了资源,这时,就需要定义自己的拷贝构造函数,使之不但拷贝成员,也分配和拷贝资源。

    这里写图片描述

  • 创建p2时,对象p1被复制给了p2,同时资源也作了复制。
  • 因此,p1和p2指向不同的资源。
  • 这,便被称为深拷贝

    如果你的类需要析构函数来析构资源,则它也需要一个拷贝构造函数。
    因为对象通常是自动被析构的。
    如果需要一个自定义的析构函数,那就意味着有额外资源要在对象被析构之前释放。
    此时,对象的拷贝就不是浅拷贝了。

  • 深拷贝代码实现

#include<iostream>
#include<string.h>
#define _CRT_SECURE_NO_WARNINGS
#pragma warning( disable : 4996)

using namespace std;

传统写法
class String
{
public:
    String(char* str = "")
        :_str(new char[strlen(str)+1])
    {
            strcpy(_str, str);
    }

    String(const String& s)
    {
        _str = new char[strlen(s._str) + 1];
        strcpy(_str, s._str);
    }

    String& operator=(const String& s)
    {
        if (this != &s)
        {
            delete[] _str;
            _str = new char[strlen(s._str) + 1];
            strcpy(_str, s._str);
        }
        return *this;
    }

    ~String()
    {
        if (_str)
        {
            delete[] _str;
        }
    }

    char* GetStr()
    {
        return _str;
    }

private:
    char* _str;
};


//现代写法
class String
{
public:
    String(char* str)
        :_str(new char[strlen(str) + 1])
    {
        strcpy(_str, str);
    }

    String(const String& s)
        :_str(NULL)
    {
        String tmp(s._str);
        swap(_str, tmp._str);
    }

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

    ~String()
    {
        if (_str)
        {
            delete[] _str;
        }
    }

    char* GetStr()
    {
        return _str;
    }
private:
    char* _str;
};

写时拷贝

  • 基于浅拷贝和深拷贝延伸出的写时拷贝具有更好的实用性。
  • 在只需要进行 只读 操作时,执行浅拷贝。
  • 而在需要进行 写 操作时,则执行深拷贝。
  • 写时拷贝有两种方案可以实现,可根据自身需要进行取舍。
方案一
  • 增加一个类成员 _refCountPtr,用来表示同一个堆有几个对象指向。
  • 以便调用析构函数,防止空间被多次释放,或者部分空间未释放。
namespace COW1
{
    class String
    {
    public:
        String(const char*str)  //构造函数
            :_refCountPtr(new int(1))
        {
            _size = strlen(str);
            _capacity = _size;
            _str = new char[strlen(str) + 1];
            strcpy(_str, str);
        }

        String(const String& s) //拷贝构造函数
            :_str(s._str)
            , _size(s._size)
            , _capacity(s._capacity)
            , _refCountPtr(s._refCountPtr)
        {
            (*_refCountPtr)++;
        }

        ~String()  //析构函数
        {
            Release();
        }

        //s1 = s2
        String& operator=(const String& s)  //运算符的重载
        {
            if (_str != s._str)
            {
                Release();

                _str = s._str;
                _refCountPtr = s._refCountPtr;
                (*_refCountPtr)++;
            }

            return *this;
        }

        void Release()
        {
            if (--(*_refCountPtr) == 0)
            {
                cout << "Release!" << endl;
                delete[] _str;
                delete _refCountPtr;
            }
        }

        void CopyOnWrite()  //写时拷贝
        {
            if (*_refCountPtr > 1)
            {
                char* NewStr = new char[_capacity + 1];
                strcpy(NewStr, _str);

                (*_refCountPtr)--;

                _str = NewStr;
                _refCountPtr = new int(1);
            }
        }

        char& operator[](size_t pos)
        {
            CopyOnWrite();

            return _str[pos];
        }

        char operator[](size_t pos) const
        {
            return _str[pos];
        }

        const char* c_str()
        {
            return _str;
        }

    private:
        char* _str;
        int*  _refCountPtr;
        size_t _size;
        size_t _capacity;
    };
}
方案二
  • 使用引用计数,将引用计数放在字符串的头四个字节中。
  • 使得,每个对象都能有不同的引用计数。
namespace COW2
{
    class String
    {
    public:
        String(const char* str)
            :_str(new char[strlen(str)+5])
        {
            strcpy(_str+4, str);
            _str += 4;

            GetRefCount() = 1;
        }

        String(const String& s)
            :_str(s._str)
        {
            GetRefCount()++;
        }

        ~String()
        {
            Release();
        }

        void Release()
        {
            if (--GetRefCount() == 0)
            {
                delete[] (_str-4);
            }
        }

        void CopyOnWrite()
        {
            if (GetRefCount() > 1)
            {
                char* NewStr = new char[_capacity + 1];
                strcpy(NewStr, _str);

                GetRefCount()--;
                _str = NewStr;

                GetRefCount() = 1;
            }
        }

        String& operator = (const String& s)
        {
            if (_str != s._str)
            {
                Release();

                _str = s._str;
                GetRefCount()++;
            }
            return *this;
        }

        char& operator[](size_t pos)
        {
            CopyOnWrite();

            return _str[pos];
        }

        char operator[](size_t pos)const
        {
            return _str[pos];
        }

        int& GetRefCount()
        {
            return *((int*)(_str - 4));
        }

        const char* c_str()
        {
            return _str;
        }

        const char* GetStr()
        {
            return _str;
        }
    private:
        char* _str;
        size_t _size;
        size_t _capacity;
    };
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值