String —— 写时拷贝技术(Copy-On-Write)
1.往磁盘里写东西时,其实是先写到内存,只有当我们关闭文件或刷新时,才会真正写到磁盘。
2.把某个对象的值赋值给另一个变量时,这个变量默认指向与原来对象相同的空间,只有真正修改其当中的内容时,才会对其分配空间。
int main()
{
string s1("hello");
string s2 = s1;
printf("s1 address :%p\n", s1.c_str());
printf("s2 address :%p\n", s2.c_str());
s1[0] = 'H';
s2[0] = 'W';
printf("Copy-On-Write :\n");
printf("s1 address :%p\n", s1.c_str());
printf("s2 address :%p\n", s2.c_str());
return 0;
}
从上面的代码中我们可以看出string的写时拷贝的机制,只有当写时才会给其分配空间。就是下面这种样子。
写时拷贝技术是通过"引用计数"实现的,有两种方法:
-
一种是除了数组外,再重新开辟4个字节的空间(pCount),用来记录有多少个指针指向这块空间。
-
另一种是,在数组的头上多分配4个字节的,用来记录有多少个指针指向块空间。
当有新的指针指向这块空间时,引用计数+1,当要释放这块空间时,引用计数-1(假装释放),直到引用计数减为0时,才会真的释放掉这块空间。当指向的指针要改变这块空间的值时,就会为这个指针分配自己的空间,这时引用计数会发生变化,旧的空间的引用计数-1,新分配的空间引用计数+1。
第一种方法:
//_str //_pCount
class String
{
public:
String(const char* str)
:_str(new char[strlen(str) + 1])
,_pCount(new int(1))
{
strcpy(_str, str);
}
//String s2(s1)
String(const String& s) //拷贝构造
:_str(s._str)
, _pCount(s._pCount)
{
(*_pCount)++;
}
//s1 = s2
String& operator=(const String& 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* newstr = new char[strlen(_str) + 1];
strcpy(newstr, _str);
--(*_pCount);
_str = newstr;
_pCount = new int(1);
}
}
char& operator[](size_t pos)
{
CopyOnWrite();
return _str[pos];
}
const char* c_str()
{
return _str;
}
private:
char* _str;
int * _pCount;
};
第二种方法:
class String
{
public:
String(const char* str)
:_str(new char[strlen(str) + 5])
{
_str += 4;
strcpy(_str, str);
GetRefCount() = 1;
}
//s2(s1)
String(const String& s)
:_str(s._str)
{
GetRefCount()++;
}
//s1 = s2
String& operator=(const String& s)
{
if (_str != s._str)
{
if (--GetRefCount() == 0)
{
delete[](_str - 4);
}
_str = s._str;
GetRefCount()++;
}
return *this;
}
void CopyOnWrite()
{
if (GetRefCount() > 1)
{
--GetRefCount();
char* tmp = new char[strlen(_str) + 5];
tmp += 4;
strcpy(tmp, _str);
_str = tmp;
GetRefCount() = 1;
}
}
char& operator[](size_t pos)
{
CopyOnWrite();
return _str[pos];
}
char& operator[](size_t pos) const
{
return _str[pos];
}
int& GetRefCount()
{
return *((int*)(_str - 4));
}
~String()
{
if (--GetRefCount() == 0)
{
delete[](_str - 4);
}
}
private:
char* _str;
};