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

来源:互联网 发布:09和冷冷的故事 知乎 编辑:程序博客网 时间:2024/06/13 19:32

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

浅拷贝

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

    这里写图片描述

  • 创建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;    };}
原创粉丝点击