C++学习笔记4----写时复制技术1(关于引用计数的使用)

首先我们了解一下什么是写时复制:Scott Meyers推荐我们,在真正需要一个存储空间时才去声明变量(分配内存),这样会得到程序在运行时最小的内存花销,执行到那才会去做分配内存这种比较耗时的工作,这会给我们的程序在运行时有比较好的性能。写时复制Copy-On- Write)技术,是编程界“懒惰行为”——拖延战术的产物。

以std::string类为例,我们考虑以下问题:

1. 写时复制的原理是什么?

2.string类在什么情况下才共享内存?

3.string类在什么情况下才触发写时复制?

Copy-On-Write一定使用了“引用计数,必然有一个变量类似于RefCnt; 当第一个string对象str1构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它string对象复制str1时,这个RefCnt会自动加1; 当有对象析构时,这个计数会减1,直到最后一个对象析构时,RefCnt0,此时,程序才会真正的释放这块从堆上分配的内存。

那么第一个问题来了:RefCnt存放在哪儿呢?

如果存放在string类中,那么每个string的实例都各自拥有自己的RefCnt,根本不能共有一个 RefCnt;

如果是声明成全局变量,或是静态成员,那就是所有的string类共享一个了,这也不行。

结论是我们可以将 size(str) + 1, 最后一位用来存放引用计数,这样对于每一个不同的str实例,都会有相应的引用计数,而且很方面来读写。

第二个问题:string类在什么情况下才共享内存?

1)以一个对象构造自己(复制构造函数)

–只需要在string类的拷贝构造函数中做点处理,让其引用计数累加

2)以一个对象赋值(重载赋值运算符)

第三个问题:string类在什么情况下触发写时才拷贝?

在共享同一块内存的类发生内容改变时,才会发生Copy-On-Write。

 

接下来用代码展示如何实现引用计数:

 

#include 
#include 
#include 
using namespace std;

class String
{
public:
	String();
	String(const char * pstr);
	String(const String & rhs);
	String & operator=(const String & rhs);

	const char * c_str() const
	{
		return _pstr;
	}

	char & operator[](size_t idx);

	size_t size() const
	{
		return strlen(_pstr);
	}

	size_t refcount() const
	{
		return _pstr[size() + 1];
	}

	friend std::ostream & operator<<(std::ostream & os, const String & rhs);

	~String();

private:
	//在最末尾多加一位用来存放引用计数
	void init_refcount()
	{
		_pstr[size() + 1] = 1;
	}

	void increaseRefcount()
	{
		++_pstr[size() + 1];
	}

	void decreaseRefcount()
	{
		--_pstr[size() + 1];
	}
private:
	char * _pstr;
};

String::String()
: _pstr(new char[2]())
{
	cout << "String()" << endl;
	init_refcount();
}

String::~String()
{
	decreaseRefcount();
	if(refcount() == 0)
	{
		delete [] _pstr;
		cout << "~String()" << endl;
	}
}

String::String(const char * pstr)
: _pstr(new char[strlen(pstr) + 2]())
{
	cout << "String(const char *)" << endl;
	strcpy(_pstr, pstr);
	init_refcount();
}

String::String(const String & rhs)
: _pstr(rhs._pstr)
{
	increaseRefcount();
}

String & String::operator=(const String & rhs)
{
	if(this != &rhs)
	{	
		decreaseRefcount();
		if(refcount() == 0)
		{
			delete [] _pstr;
			cout << "delete lhs._pstr" << endl;
		}
		_pstr = rhs._pstr;
		increaseRefcount();
	}
	return *this;
}

//[]运算符无法区分读写操作,如何解决?
char & String::operator[](size_t idx)
{
	if(idx < size())
	{
		if(refcount() > 1)
		{
			decreaseRefcount();
			char * ptemp = new char[size() + 2]();
			strcpy(ptemp, _pstr);

			_pstr = ptemp;
			init_refcount();
		}
		return _pstr[idx];
	}
	else
	{
		static char nullchar = '\0';
		cout << "下标越界" << endl;
		return nullchar;
	}
}

std::ostream & operator<<(std::ostream & os, const String & rhs)
{
	os << rhs._pstr;
	return os;
}

int main()
{
	String str;
	cout << str << endl;
	cout << "str's refcount = " << str.refcount() << endl;
	printf("&str = %p\n", str.c_str());

	String str2("hello,world");
	String str3(str2);
	cout << "str2 = " << str2 << endl;
	cout << "str3 = " << str3 << endl;
	cout << "str2's refcount = " << str2.refcount() << endl;
	cout << "str3's refcount = " << str3.refcount() << endl;
	printf("&str2 = %p\n", str2.c_str());
	printf("&str3 = %p\n", str3.c_str());
	cout << endl;

	str = str2;//str引用计数为1
	cout << "str = " << str << endl;
	cout << "str's refcount = " << str.refcount() << endl;
	cout << "str2's refcount = " << str2.refcount() << endl;
	cout << "str3's refcount = " << str3.refcount() << endl;
	printf("&str = %p\n", str.c_str());
	printf("&str2 = %p\n", str2.c_str());
	printf("&str3 = %p\n", str3.c_str());
	cout << "---------" << endl;

	String str4 = "wangdao";
	String str5(str4);
	cout << "str4 = " << str4 << endl;
	cout << "str5 = " << str5 << endl;
	printf("&str4 = %p\n", str4.c_str());
	printf("&str5 = %p\n", str5.c_str());
	cout << "str4's refcount = " << str4.refcount() << endl;
	cout << "str5's refcount = " << str5.refcount() << endl;
	cout << endl;

	str4 = str2;//str4引用计数为2
	cout << "str = " << str << endl;
	cout << "str's refcount = " << str.refcount() << endl;
	cout << "str2's refcount = " << str2.refcount() << endl;
	cout << "str3's refcount = " << str3.refcount() << endl;
	cout << "str4's refcount = " << str4.refcount() << endl;
	cout << "str5's refcount = " << str5.refcount() << endl;
	printf("&str = %p\n", str.c_str());
	printf("&str2 = %p\n", str2.c_str());
	printf("&str3 = %p\n", str3.c_str());
	printf("&str4 = %p\n", str4.c_str());
	printf("&str5 = %p\n", str5.c_str());

	cout << endl << "----做写操作" << endl;
	str[0] = 'X';
	cout << "str = " << str << endl;
	cout << "str's refcount = " << str.refcount() << endl;
	cout << "str2's refcount = " << str2.refcount() << endl;
	cout << "str3's refcount = " << str3.refcount() << endl;
	cout << "str4's refcount = " << str4.refcount() << endl;

	printf("&str = %p\n", str.c_str());
	printf("&str2 = %p\n", str2.c_str());
	printf("&str3 = %p\n", str3.c_str());
	printf("&str4 = %p\n", str4.c_str());

	cout << endl << "---做读操作" << endl;
	cout << str2[0] << endl;

	cout << "str2 = " << str2 << endl;
	cout << "str3 = " << str3 << endl;
	cout << "str4 = " << str4 << endl;
	cout << "str2's refcount = " << str2.refcount() << endl;
	cout << "str3's refcount = " << str3.refcount() << endl;
	cout << "str4's refcount = " << str4.refcount() << endl;

	printf("&str2 = %p\n", str2.c_str());
	printf("&str3 = %p\n", str3.c_str());
	printf("&str4 = %p\n", str4.c_str());

	return 0;
}

你可能感兴趣的:(C++学习笔记4----写时复制技术1(关于引用计数的使用))