写时拷贝又叫引用计数的浅拷贝,浅拷贝不同于深拷贝的既拷空间又拷数据,浅拷贝只是拷贝数据,每开辟一块空间,随之有一个计数器,当有多个对象指向该空间时,引用计数++,每销毁一个对象,引用计数–,当引用计数为1时,才真正的销毁这块空间。
第一种实现:开辟一块空间的同时,再开辟一块空间存放引用计数。
//写时拷贝(第一种)
class String
{
public:
String(char* str = "")
:_str(new char[strlen(str)+1])
,_refCount(new int(1))
{
strcpy(_str, str);
}
String(const String& s)
:_str(s._str)
,_refCount(s._refCount)
{
++(*_refCount);
}
String& operator=(const String& s)
{
if(_str != s._str)
{
Release();
_str = s._str;
_refCount = s._refCount;
++(*_refCount);
}
return *this;
}
/*String& operator=(String& s)
{
swap(_str, s._str);
swap(_refCount, s._refCount);
return *this;
}*/
void Release()
{
if(--(*_refCount) == 0)
{
cout<<"delete"<<endl;
delete[] _str;
delete _refCount;
}
}
char& operator[](size_t index)
{
assert(index < strlen(_str));
return _str[index];
}
char operator[](size_t index) const
{
return _str[index];
}
~String()
{
Release();
}
const char* GetStr()
{
return _str;
}
private:
char* _str;
int* _refCount;//引用计数
};
//测试用例
void TestCopyOnWrite1()
{
String s1("hello cat!");
String s2(s1);
String s3(s1);
s3 = s1;
cout<<s1[2]<<endl;
cout<<s2.GetStr()<<endl;
cout<<s3.GetStr()<<endl;
}
第二种实现:开辟一块空间,将头4个字节用于存放计数器,后面存放对象。
//写时拷贝(第二种)
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str)+5])
{
strcpy(_str+4,str);//从第五个位置开始拷贝
_str = _str + 4;//_str为开始保存数据的位置
GetRefCount() = 1;
}
String(const String& s)
{
_str = s._str;
++GetRefCount();
}
String& operator=(const String& s)
{
if(_str != s._str)
{
Release();
_str = s._str;
++(GetRefCount());
}
return *this;
}
void Release()
{
if(--GetRefCount() == 0)
delete[] (_str-4);
}
~String()
{
Release();
}
char& operator[](size_t index)
{
assert(index < strlen(_str));
return _str[index];
}
char operator[](size_t index) const
{
return _str[index];
}
int& GetRefCount()
{
return *((int*)(_str-4));//返回_str头部前4个字节的引用计数
}
char* GetStr() const
{
return _str;
}
private:
char* _str;
};
void TestCopyOnWrite2()
{
String s1("hello cat!");
String s2(s1);
String s3("beautiful cat");
s1 = s3;
cout<<s1.GetStr()<<endl;
cout<<s1.GetRefCount()<<endl;
cout<<s2.GetStr()<<endl;
cout<<s2.GetRefCount()<<endl;
cout<<s3.GetStr()<<endl;
cout<<s3.GetRefCount()<<endl;
}