在深浅拷贝中提到的深浅拷贝也有自己的缺陷。我们知道浅拷贝可能造成重新出错,深拷贝又会浪费空间。因为并不是每个要拷贝的对象都会修改数据,如果并不准备修改数据,浅拷贝进行读也是可以的。所以就有了写时拷贝。它具体实现思想是:先对原始对象进行浅拷贝,若是要修改数据就再调用深拷贝,让它修改自己的数据,不会造成原始对象受到影响的情况。
我们将写时拷贝认为是使用引用计数实现的浅拷贝。我们在开辟空间时会多开辟4字节用以保存引用技术pcount的值。在有其他对象需要这块内存时,pcount++,在有对象析构时,pcount–。直至只有一个对象指向这块内存,在这个对象使用完之后,我们就可以真正的free这块堆上分配的内存。
具体实现代码如下:
class String
{
public:
//构造函数
String(const char* str = "")
{
_str = new char[strlen(str)+1];
strcpy(_str, str);
_pcount = new int(1);
}
//s2(s1)
String(const String& s)//浅拷贝构造
{
_str = s._str;
_pcount = s._pcount;
++(*_pcount);
}
//s1 = s2
String& operator=(const String& s)//赋值运算符
{
//if(this != &s)
if(_str != s._str)
{
if(--(*_pcount) == 0)
{
delete[] _str;
delete _pcount;
}
_str = s._str;
_pcount = s._pcount;
++(*_pcount);
}
return *this;
}
~String()//析构函数
{
//要保证当前对象是唯一一个使用它的,才可以释放内存
if(--(*_pcount) == 0)
{
delete[] _str;
delete _pcount;
}
}
void CopyOnWrite()//写时拷贝
{
if(*_pcount > 1)
{
char* tmp = new char[strlen(_str)+1];
strcpy(tmp, _str);
--(*_pcount);
_str = tmp;
_pcount = new int(1);
}
}
char& operator[](size_t pos)//重载[]
{
CopyOnWrite();
return _str[pos];
}
const char& operator[](size_t pos) const
{
return _str[pos];
}
char* c_str()
{
return _str;
}
private:
char* _str;
//引用计数要实现内存共享,所以它应该是在堆上的数据,每个对象都有一个指向它的指针
int* _pcount;//引用计数,使用int*可以使其指向同一块空间,否则只有一个对象能看到当前的引用计数
size_t _size;
size_t _capacity;
};