写时拷贝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;
}
}