C++写时拷贝

来源:互联网 发布:四层横移编程 编辑:程序博客网 时间:2024/06/05 23:01

写时拷贝copy on write

**首先什么时候会用到写时拷贝呢?
答:顾名思义修改的时候才拷贝,是为了解决浅拷贝的坑和平衡内存开销大而出的解决方案。

浅拷贝的坑——就是多个对象共用同一地址,在析构时同一块空间被释放多次,这种情况是不被允许的。

内存开销大——就是在每一次 深拷贝 时系统会新开辟空间,用来存放新对象。每创建一个新对象,就会开辟空间。每新开辟空间自然就会占内存,就会存在内存开销╮(╯_╰)╭。

深拷贝——就是为了“填浅拷贝的坑”,为新对象开辟新空间。

这里写图片描述

为了防止多次析构,但是还要考虑内存开销。我们想到用 引用计数(_refCount) 来起到一个内存使用的记录作用,让使用者清楚这块空间被who共用。在这块空间被析构时保证只有一个对象使用,即引用计数是1。当引用计数为0,这块空间真正被释放。

引用计数——即防止多个对象用一块空间时,这块空间被多次释放,用到的一个计数器。

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<cstdio>using namespace std;namespace COW{    class String    {    public:        String(const char* str)            :_str(new char[strlen(str) + 1])            , _refCount(new int(1))        {            strcpy(_str, str);        }        // s2(s1)        String(String& s)        {            _str = s._str;            _refCount = s._refCount;            ++(*_refCount);        }        // s2 = s4        String& operator=(const String& s)        {            if (_str != s._str)            {                Release();                _str = s._str;                _refCount = s._refCount;                (*_refCount)++;            }            return *this;        }        void Release()        {            if (--(*_refCount) == 0)            {                cout << "delete" << endl;                delete[] _str;                delete _refCount;            }        }        char& operator[](size_t pos)        {            CopyOnWrite();            return _str[pos];        }        char operator[](size_t pos) const        {            return _str[pos];        }        void CopyOnWrite()        {            if (*_refCount > 1)            {                char* tmp = new char[strlen(_str) + 1];                strcpy(tmp, _str);                --(*_refCount);                _str = tmp;                _refCount = new int(1);            }        }        // Insert        // Erase        ~String()        {            Release();        }    private:        char* _str;        int* _refCount;    };

这里测试时:

void TestString()    {        String s1("hello world");        String s2(s1);        String s3(s1);        String s4("aaaaaaaaaaa");        String s5(s4);        s2 = s4;    }

这里写图片描述

对于这个例子,我们还可以做一个优化,可以模仿new[]底层实现来开辟空间,让引用计数占用前四个字节的空间 。

这里写图片描述

优化代码如下:

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<cstdio>using namespace std;namespace COW{    class String    {    private:        char*_str;    public:        String(const char* str)            :_str(new char[strlen(str) + 5])        {            //*((int*)_str) = 1;            _str += 4;            strcpy(_str, str);            GetRefCount()=1;        }        int& GetRefCount()        {            return *((int*)(_str - 4));        }        ~String()        {            //if (--*((int*)_str - 4) == 0)            if (--GetRefCount()==0)            {                cout << "delete" << endl;                delete[](_str - 4);            }        }        String(const String&s)            :_str(s._str)        {            //(*(int*)(_str - 4))++;            ++GetRefCount();        }        String& operator=(const String& s)        {            if (_str != s._str)            {                if (--GetRefCount() == 0)                {                    delete[](_str - 4);                }                _str = s._str;                GetRefCount()++;            }            return *this;        }        const  char* GetStr()        {            return _str;        }    };    void TestString()    {        String s1("hello");        cout << s1.GetStr() << endl;        String s2 = s1;        cout << s2.GetStr() << endl;    }}
原创粉丝点击