string类的写时复制COW技术(copy on write)

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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值