c++实现string的写时复制技术(COW)——注释超细版
COW(copy-on-write)
前言
当字符串进行复制的时候,如很长的字符串(2k大小),如果全部采用堆空间存储的话那是非常浪费空间的,复制一次变成4k大小,两次6k…
所以为了节约空间,在两个字符串都是相同内容的时候,将复制后的指针指向原始字符串的地址空间,然后采用引用计数的方式对该空间的引用数+1;当修改某一指针的内容时,其实就是改变了字符串数据,那么显然变成了一个新的字符串,这时候就需要给这个新的字符串重新申请一块堆空间了,然后对原始字符串的引用计数-1。
代码实现
#include <iostream>
#include <string.h>
using namespace std;
class String
{
private:
char *_pstr;
public:
String()
: _pstr(new char[5]() + 4)
{
*(int *)(_pstr - 4) = 1;
cout << "String()" << endl;
}
String(const char *pstr)
: _pstr(new char[strlen(pstr) + 5]() + 4)
{
strcpy(_pstr, pstr);
*(int *)(_pstr - 4) = 1;
cout << "String(const char *pstr)" << endl;
}
String(const String &rhs)
: _pstr(rhs._pstr)
{
++*(int *)(_pstr - 4);
cout << "String(const String &rhs)" << endl;
}
String &operator=(const String &rhs)
{
cout << "String operator=(const String &rhs)" << endl;
if (this != &rhs)
{
if (1 == *(int *)(_pstr - 4))
{
delete[](_pstr - 4);
}
_pstr = rhs._pstr;
++*(int *)(_pstr - 4);
}
return *this;
}
~String()
{
if (1 == *(int *)(_pstr - 4))
{
delete[](_pstr - 4);
_pstr = nullptr;
}
cout << "~String()" << endl;
}
class charProxy
{
private:
String &_self;
size_t _idx;
public:
charProxy(String &self, size_t idx)
: _self(self), _idx(idx) {}
char &operator=(const char &rhs)
{
cout << "char &operator=(const char &rhs)" << endl;
if (_idx < _self.size() && _idx >= 0)
{
if (_self.refCount() > 1)
{
char *ptmp = new char[_self.size() + 5]() + 4;
strcpy(ptmp, _self._pstr);
--*(int *)(_self._pstr - 4);
_self._pstr = ptmp;
*(int *)(_self._pstr - 4) = 1;
}
_self._pstr[_idx] = rhs;
return _self._pstr[_idx];
}
else
{
cout << "error idx" << endl;
static char nullchar = '\0';
return nullchar;
}
}
operator char()
{
return _self._pstr[_idx];
}
};
charProxy operator[](size_t idx)
{
return charProxy(*this, idx);
}
size_t size() const
{
return strlen(_pstr);
}
int refCount() const
{
return *(int *)(_pstr - 4);
}
const char *strAddr() const
{
return _pstr;
}
friend ostream &operator<<(ostream &os, const String &rhs);
};
ostream &operator<<(ostream &os, const String &rhs)
{
if (rhs._pstr)
{
os << rhs._pstr;
}
return os;
}
int main(int argc, char **argv)
{
String s1("hello");
cout << "s1 = " << s1 << endl;
cout << "s1.refcount = " << s1.refCount() << endl;
printf("s1.addr = %p\n", s1.strAddr());
cout << endl;
cout << "拷贝操作String s2 = s1" << endl;
String s2 = s1;
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
cout << "s1.refcount = " << s1.refCount() << endl;
printf("s1.addr = %p\n", s1.strAddr());
cout << "s2.refcount = " << s2.refCount() << endl;
printf("s2.addr = %p\n", s2.strAddr());
s2.strAddr();
cout << endl;
cout << "赋值操作s3 = s2" << endl;
String s3;
s3 = s2;
cout << "s2.refcount = " << s2.refCount() << endl;
printf("s2.addr = %p\n", s2.strAddr());
cout << "s3.refcount = " << s3.refCount() << endl;
printf("s3.addr = %p\n", s3.strAddr());
cout << endl;
cout << "读下标操作" << endl;
cout << "s2[0] = " << s2[0] << ", s1 = " << s1 << endl;
cout << "s1.refcount = " << s1.refCount() << endl;
printf("s1.addr = %p\n", s1.strAddr());
cout << "s2.refcount = " << s2.refCount() << endl;
printf("s2.addr = %p\n", s2.strAddr());
cout << endl;
cout << "对数据写操作,将会触发写时复制技术!" << endl;
s2[0] = 'H';
cout << "s2[0] = " << s2[0] << ", s2 = " << s2 << endl;
cout << "s1.refcount = " << s1.refCount() << endl;
printf("s1.addr = %p\n", s1.strAddr());
cout << "s2.refcount = " << s2.refCount() << endl;
printf("s2.addr = %p\n", s2.strAddr());
cout << "s3.refcount = " << s3.refCount() << endl;
printf("s3.addr = %p\n", s3.strAddr());
return 0;
}
运行结果
注意看内存地址变化,及引用计数的变化
String(const char *pstr)
s1 = hello
s1.refcount = 1
s1.addr = 0x55d2299c0e74
拷贝操作String s2 = s1
String(const String &rhs)
s1 = hello
s2 = hello
s1.refcount = 2
s1.addr = 0x55d2299c0e74
s2.refcount = 2
s2.addr = 0x55d2299c0e74
赋值操作s3 = s2
String()
String operator=(const String &rhs)
s2.refcount = 3
s2.addr = 0x55d2299c0e74
s3.refcount = 3
s3.addr = 0x55d2299c0e74
读下标操作
s2[0] = h, s1 = hello
s1.refcount = 3
s1.addr = 0x55d2299c0e74
s2.refcount = 3
s2.addr = 0x55d2299c0e74
对数据写操作,将会触发写时复制技术!
char &operator=(const char &rhs)
s2[0] = H, s2 = Hello
s1.refcount = 2
s1.addr = 0x55d2299c0e74
s2.refcount = 1
s2.addr = 0x55d2299c12a4
s3.refcount = 2
s3.addr = 0x55d2299c0e74
~String()
~String()
~String()
如果本文对你有帮助,记得一键三连哦,一键三连笑哈哈,代码能力顶呱呱!
本人能力有限,如有错误,望不吝指正;原创不易,欢迎转载,转载请注明出处!
|