string类的写时复制COW技术(copy on write)
文章目录
一.COW写时复制技术
string s1("s1");
string s2(s1);
string s3(s1);
cout << s1[0] << endl;//不改变
s1[0] = 'S';//需要重新申请内存区域
先看上面的代码,如果这三个相同的string类都要申请空间的话,对于内存空间的消耗无疑是巨大的,所以在string类的内部实现中,s1,s2,s3实际上指向的是同一块内存空间。
string内部有一块内存空间就,也就是const char类型,由于s1,s2,s3都指向它,所以就有了引用计数(RefCount),其引用技术为3(用法类似于智能指针),因为int是四个字节,将const char 前面四个字节设置为引用计数存放的后面的位置,从第五个字节开始才进行存储字符串数据。
cout << s1[0] << endl;//不改变
s1[0] = 'S';//需要重新申请内存区域
在进行读取的时候不改变内存,但当要进行写操作的时候,那么需要重新申请一块新的内存(并将字符串进行拷贝,引用计数初始化为1),并将原内存区域的引用计数减一。也就是s2,s3的内存地址不变,引用计数均减为2,s1重新申请空间,并且初始化引用计数为1,复制原地址内容,实现s1[0] = 'S'
二.引用计数(RefCount)实现
利用C语言的指针可以进行内存偏移。
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
using namespace std;
//1.将int型的四个字节存入char数组中
void test0(){
char *s = new char[5];
int i =3;
memcpy(s, (void*)&i, 4);
cout << (int)*s << endl;
(*s)++;
cout << (int)*s << endl;
(*s) += 5;
cout << (int)*s << endl;
}
//2.char数组指针进行偏移
void test1(){
char *s = new char[5]();
*(s+3) = 'a';
for (int i = 0; i < 5; i++){
cout << s[i] << endl;
}
}
int main()
{
test1();
return 0;
}
三.String类实现(无代理模式Proxy版)
这里需要注意的一点就是,对于运算符[],我们并没有办法判断是将进行读操作还是写操作,所以在不适用代理模式的操作下,统一将其设置为写操作。
#include <iostream>
#include <cstdio>
#include <cstring>
using std::cout;
using std::endl;
const int offset = sizeof(int);
/*****************用string类实现copy on write(写时复制)**********************/
/********************************string类申明*********************************/
class String
{
public:
String();
String(const char *);
String(const String &);
~String();
String &operator=(const char *);
String &operator=(const String &);
String &operator= (const char& c);
char &operator[](int);
char *c_str();
void increaseRefcnt();
void decreaseRefcnt();
void initRefcnt();
void release();
int Refcnt();
int size();
private:
char *_pstr;
};
/********************************string类定义*********************************/
#if 0
//运算符[] 统一按读操作来
char &String::operator[](int index)
{
if (index < 0 || index >= size())
{
static char c = '\0';
return c;
}
return c_str()[index];
}
String& String::operator= (const char& c){
decreaseRefcnt();
_pstr = new char[4 + strlen(str) + 1];
initRefcnt();
memcpy(_pstr + 4, str, strlen(str));
}
#endif
#if 1
//注意运算符[],系统并不能判断是要进行读操作还是写操作,所以此处统一按写操作来
char &String::operator[](int index)
{
if (index < 0 || index >= size())
{
static char c = '\0';
return c;
}
else
{
if (Refcnt() == 1)
{
return _pstr[4 + index];
}
else
{
decreaseRefcnt();
char *temp = _pstr;
_pstr = new char[size() + 4 + 1];
initRefcnt();
strcpy(_pstr + 4, temp + 4);
return _pstr[4 + index];
}
}
}
#endif
//
String &String::operator=(const String &lhs)
{
if (this != &lhs)
{
decreaseRefcnt();
_pstr = lhs._pstr;
increaseRefcnt();
}
return *this;
}
String &String::operator=(const char *str)
{
decreaseRefcnt();
_pstr = new char[4 + strlen(str) + 1];
initRefcnt();
memcpy(_pstr + 4, str, strlen(str));
}
String::String()
: _pstr(new char[4])
{
initRefcnt();
}
//构造函数:initRefcnt
String::String(const char *str)
: _pstr(new char[4 + strlen(str) + 1])
{
initRefcnt();
memcpy(_pstr + 4, str, strlen(str));
}
//拷贝构造函数:increaseRefcnt
String::String(const String &lhs)
{
_pstr = lhs._pstr;
increaseRefcnt();
}
String::~String()
{
release();
}
char *String::c_str()
{
return _pstr + 4;
}
int String::Refcnt()
{
return (int)*_pstr;
}
void String::release()
{
if (_pstr)
{
delete[] _pstr;
_pstr = NULL;
}
}
void String::decreaseRefcnt()
{
(*_pstr)--;
if ((*_pstr) == 0)
{
release();
}
}
void String::increaseRefcnt()
{
(*_pstr)++;
}
void String::initRefcnt()
{
int val = 1;
memcpy(_pstr, (void *)&val, offset);
}
int String::size()
{
return strlen(_pstr + 4);
}
/********************************测试函数*********************************/
void test0()
{
String s1("s1");
cout << s1.c_str() << endl;
String s2(s1);
cout << s2.c_str() << endl;
cout << s1.Refcnt() << endl;
cout << s2.Refcnt() << endl;
}
void test1()
{
String s1("s1");
String s2(s1);
cout << s2.c_str() << endl;
cout << s2.Refcnt() << endl;
String s3("s3");
s2 = s3;
cout << s2.c_str() << endl;
cout << s2.Refcnt() << endl;
cout << s1.c_str() << endl;
cout << s1.Refcnt() << endl;
}
void test2()
{
String s;
s = "s";
cout << s.c_str() << endl;
}
void test3()
{
String s1("s1");
cout << s1.c_str() << endl;
printf("s1:%p\n", s1.c_str());
String s2(s1);
cout << s2.c_str() << endl;
printf("s2:%p\n", s2.c_str());
//s2[0] = 'S';
cout << s2.c_str() << endl;
printf("s2:%p\n", s2.c_str());
}
void test4()
{
String s1("s1");
char c = s1[0];
cout << c << endl;
}
int main()
{
test3();
return 0;
}
四.String类实现(代理模式Proxy版)
上面说过了,只要是有运算符[]就进行重新申请内存很明显是不明智的,所以我们需要用到代理模式
,也就是将运算符[]交给一个类Charproxy进行处理,由这个类Charproxy来帮我们判断[]究竟是读操作还是写操作。
Charproxy类的内部,如果要进行写操作,后面会紧跟 = 符号进行重新赋值,所以这就可以区分写操作了。只要后面没有紧跟 = 符号的话,很明显就是读操作了。
#include <iostream>
#include <cstdio>
#include <cstring>
using std::cout;
using std::endl;
const int offset = sizeof(int);
/*****************用string类实现copy on write(写时复制)**********************/
/********************************string类申明*********************************/
class String
{
public:
class Charproxy
{
public:
Charproxy(String& s, int index)
: _string(s), _index(index)
{
}
//写操作
Charproxy &operator=(const char &c)
{
char *temp = new char[_string.size() + 1];
strcpy(temp, _string.c_str());
_string.decreaseRefcnt();
char *temp2 = new char();
_string._pstr = new char[4 + strlen(temp) + 1];
_string.initRefcnt();
strcpy(_string.c_str(), temp);
_string.c_str()[_index] = c;
delete[] temp;
delete[] temp2;
return *this;
}
//读操作
operator char()
{
return _string.c_str()[_index];
}
private:
String &_string;
int _index;
};
public:
String();
String(const char *);
String(const String &);
~String();
String &operator=(const char *);
String &operator=(const String &);
//运算符[] 交给Charproxy代理进行处理
Charproxy operator[](int index)
{
return Charproxy(*this, index);
}
char *c_str();
void increaseRefcnt();
void decreaseRefcnt();
void initRefcnt();
void release();
int Refcnt();
int size();
private:
char *_pstr;
};
/********************************string类定义*********************************/
#if 0
//注意运算符[],系统并不能判断是要进行读操作还是写操作,所以此处统一按写操作来
char &String::operator[](int index)
{
if (index < 0 || index >= size())
{
static char c = '\0';
return c;
}
else
{
if (Refcnt() == 1)
{
return _pstr[4 + index];
}
else
{
decreaseRefcnt();
char *temp = _pstr;
_pstr = new char[size() + 4 + 1];
initRefcnt();
strcpy(_pstr + 4, temp + 4);
return _pstr[4 + index];
}
}
}
#endif
//
String &String::operator=(const String &lhs)
{
if (this != &lhs)
{
decreaseRefcnt();
_pstr = lhs._pstr;
increaseRefcnt();
}
return *this;
}
String &String::operator=(const char *str)
{
decreaseRefcnt();
_pstr = new char[4 + strlen(str) + 1];
initRefcnt();
strcpy(_pstr + 4, str);
return *this;
}
String::String()
: _pstr(new char[4])
{
initRefcnt();
}
//构造函数:initRefcnt
String::String(const char *str)
: _pstr(new char[4 + strlen(str) + 1])
{
initRefcnt();
strcpy(_pstr + 4, str);
}
//拷贝构造函数:increaseRefcnt
String::String(const String &lhs)
{
_pstr = lhs._pstr;
increaseRefcnt();
}
String::~String()
{
release();
}
char *String::c_str()
{
return _pstr + 4;
}
int String::Refcnt()
{
return (int)*_pstr;
}
void String::release()
{
if (_pstr)
{
delete[] _pstr;
_pstr = NULL;
}
}
void String::decreaseRefcnt()
{
(*_pstr)--;
if ((*_pstr) == 0)
{
release();
}
}
void String::increaseRefcnt()
{
(*_pstr)++;
}
void String::initRefcnt()
{
int val = 1;
memcpy(_pstr, (void *)&val, offset);
}
int String::size()
{
return strlen(_pstr + 4);
}
/********************************测试函数*********************************/
void test0()
{
String s1("s1");
cout << s1.c_str() << endl;
String s2(s1);
cout << s2.c_str() << endl;
cout << s1.Refcnt() << endl;
cout << s2.Refcnt() << endl;
}
void test1()
{
String s1("s1");
String s2(s1);
cout << s2.c_str() << endl;
cout << s2.Refcnt() << endl;
String s3("s3");
s2 = s3;
cout << s2.c_str() << endl;
cout << s2.Refcnt() << endl;
cout << s1.c_str() << endl;
cout << s1.Refcnt() << endl;
}
void test2()
{
String s;
s = "s";
cout << s.c_str() << endl;
}
void test3()
{
String s1("s1");
cout << s1.c_str() << endl;
printf("s1:%p\n", s1.c_str());
String s2(s1);
cout << s2.c_str() << endl;
printf("s2:%p\n", s2.c_str());
//s2[0] = 'S';
cout << s2.c_str() << endl;
printf("s2:%p\n", s2.c_str());
}
void test4()
{
String s1("s1");
String s2(s1);
String s3(s2);
cout << "s1[0] = " << s1[1] << endl;
cout << "s1:" << s1.c_str() << endl;
cout << "s1.refcnt:" << s1.Refcnt() << endl;
printf("s1:%p\n", s1.c_str());
cout << "s2:" << s2.c_str() << endl;
cout << "s2.refcnt:" << s2.Refcnt() << endl;
printf("s2:%p\n", s2.c_str());
cout << "s3:" << s3.c_str() << endl;
cout << "s3.refcnt:" << s3.Refcnt() << endl;
printf("s3:%p\n", s3.c_str());
cout << "after write=======================" << endl;
s1[0] = 'S';
cout << "s1[0] = " << s1[0] << endl;
cout << "s1[0] = " << s1[1] << endl;
cout << "s1:" << s1.c_str() << endl;
cout << "s1.refcnt:" << s1.Refcnt() << endl;
printf("s1:%p\n", s1.c_str());
cout << "s2:" << s2.c_str() << endl;
cout << "s2.refcnt:" << s2.Refcnt() << endl;
printf("s2:%p\n", s2.c_str());
cout << "s3:" << s3.c_str() << endl;
cout << "s3.refcnt:" << s3.Refcnt() << endl;
printf("s3:%p\n", s3.c_str());
}
int main()
{
test4();
return 0;
}