模拟实现string——深浅拷贝
上篇文章谈到了浅拷贝的缺陷,我们可以用深拷贝和写时拷贝改善
深拷贝其实也是有缺陷的——每次的开辟空间消耗太大
下面我们来介绍一下写时拷贝
引用计数的写时拷贝是基于引用计数的浅拷贝
那么什么是写时拷贝:其实写时拷贝的意思就是: 当你读取到这个空间的时候,并不会开辟出一个一模一样的空间出来给你,当你真正需要拷贝的时候,那么他就会开辟出空间给你。
当我们要对它进行写操作是才会真正的拷贝一块内存大小
它的大致原理为
我们引用了引用计数,每次当引用计数变为一时才会释放
具体的代码如下:
class String
{
public:
String(const char* str)
:_str(new char[strlen(str) + 1])
, _refCount(new int(1))
{
strcpy(_str, str);
}
String(String &s)
{
_str = s._str;
_refCount = s._refCount;
(*_refCount)++;
}
void Relese()
{
if (--(*_refCount) == 0)
{
delete[] _str;
delete[] _refCount;
}
}
String &operator=(const String &s)
{
if (_str != s._str)
{
Relese();
_str = s._str;
_refCount = s._refCount;
(*_refCount)++;
}
return *this;
}
char &operator[](size_t pos)
{
CopyOnWrite();
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);
}
}
~String()
{
Relese();
}
private:
char*_str;
int*_refCount;
};
我们可以发现这样就可以少开辟很多空间
但是在这个程序上面refCount占用了一个空间,指向的内容占用了一个空间,我们可以对这个程序进行优化
我们可以开辟一个空间让这块空间的前四个字节存放refCount,后面存放指向的内容
每次释放的时候我们只释放引用计数后面的内容
代码如下:
#include<iostream>
#include<windows.h>
using namespace std;
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str) + 5])
{
GetRefCount() = 1;
_str += 4;
strcpy(_str, str);
}
String(String &s)
:_str(s._str)
{
GetRefCount()++;
}
String &operator=(const String &s)
{
if (_str != s._str)
{
if (--GetRefCount() == 0)
{
delete[](_str - 4);
}
_str = s._str;
GetRefCount()++;
}
return *this;
}
char& operator[](size_t index)
{
if (GetRefCount() == 1) //如果计数器为1,则直接返回
{
return _str[index];
}
GetRefCount()--;
char *tmp = _str;
_str = new char[strlen(tmp) + 1 + 4];
_str += 4;
strcpy(_str, tmp);
GetRefCount() = 1;
return _str[index];
}
int &GetRefCount()
{
return(*(int*)(_str - 4));
}
~String()
{
if (--GetRefCount() == 0)
{
delete[](_str - 4);
}
}
private:
char*_str;
};