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

标签: C++ 浅拷贝 深拷贝 写时拷贝
171人阅读 评论(0) 收藏 举报
分类:

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

浅拷贝

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

    这里写图片描述

  • 创建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;
    };
}
查看评论

由深拷贝与浅拷贝引发的引用计数、写时拷贝技术

一、理解深拷贝和浅拷贝: #include using namespace std; class String { public: String(const char *str = "") {...
  • wjxxaut
  • wjxxaut
  • 2016-08-13 21:14:58
  • 391

C++::浅拷贝,深拷贝,引用计数的拷贝,写时拷贝

【什么情况下会用到c++中的拷贝构造函数】:  1)用已经存在的同类的对象去构造出另一个新的对象   2)当函数的形参是类的对象时,这时调用此函数,使用的是值的拷贝,也会调用拷贝构造函数   3)当...
  • lalu58
  • lalu58
  • 2017-01-04 21:47:10
  • 1342

String类详解(浅拷贝,深拷贝,引用计数,写时拷贝)

String类:标准库类型string类表示可变长的字符序列,定义在std中,专门用来管理字符串,下面一起看下它的重要考点。 一,浅拷贝       所谓浅拷贝,是指原对象与拷贝对象公用一份实...
  • snow_5288
  • snow_5288
  • 2016-10-23 18:16:59
  • 4520

赋值、浅拷贝和深拷贝

直接赋值  先定义了一个变量a,然后将a变量赋值给b。则a、b两个变量都指向内存中同一个对象。public static T[] extendsArray(T[] data){ int new...
  • juny9123
  • juny9123
  • 2017-05-24 23:04:57
  • 221

Python中深拷贝与浅拷贝的区别:

Python中深拷贝与浅拷贝的区别:
  • u014745194
  • u014745194
  • 2017-04-20 16:58:35
  • 2819

深拷贝和浅拷贝的理解与应用

深拷贝和浅拷贝的理解与应用 对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部...
  • u014628388
  • u014628388
  • 2017-08-22 23:55:01
  • 1379

Python 赋值、浅拷贝、深拷贝的区别?

http://songlee24.github.io/2014/08/15/python-FAQ-02/ 在写Python过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念...
  • yugongpeng_blog
  • yugongpeng_blog
  • 2015-06-23 12:20:26
  • 4507

java的深拷贝与浅拷贝

转载:http://www.2cto.com/kf/201401/273852.htmlJava中对象的创建clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。...
  • lcg910978041
  • lcg910978041
  • 2016-07-22 10:46:02
  • 9075

【Java深入】深拷贝与浅拷贝详解

1.拷贝的引入(1)引用拷贝创建一个指向对象的引用变量的拷贝。例1:Teacher teacher = new Teacher("Taylor",26); Teacher otherteacher =...
  • baiye_xing
  • baiye_xing
  • 2017-05-13 11:12:43
  • 725

iOS开发——深拷贝与浅拷贝详解

深拷贝和浅拷贝这个问题在面试中常常被问到,而在实际开发中,只要稍有不慎,就会在这里出现问题。尤其对于初学者来说,我们有必要来好好研究下这个概念。我会以实际代码来演示,相关示例代码上传至 这里 。   ...
  • CHENYUFENG1991
  • CHENYUFENG1991
  • 2016-06-28 01:41:07
  • 9319
    个人资料
    持之以恒
    等级:
    访问量: 4564
    积分: 287
    排名: 27万+