playerken

博客园 首页 新随笔 联系 订阅 管理

STL中的string类采用了Copy On Write技术。即通过拷贝构造函数创建的对象不分配新的资源,引用原对象的资源。只有当写操作时,才分配新的资源。如下代码(GCC)演示了通过拷贝构造函数创建的string拥有相同的地址。当改变string的值时,重新分配字符串数组,地址改变。

 

#include <stdio.h>
#include <string>

using namespace std;


int main()
{
	string s1 = "11111";
	string s2 = s1;

	// s1 and s2 are referring to the same string.
	printf("%x, %x\n", s1.c_str(), s2.c_str());
	
	// Once a write operation is performed on s1, 
	// s1 will refer to a different string.
	s1[1] = 'f';
	s2[1] = 'v';
	printf("%x, %x\n", s1.c_str(), s2.c_str());

    return 0;
}

 

以string类为例:

当通过构造函数创建一个string时,new一个字符串数组长度+1的空间,用额外的空间存放引用计数。

当通过拷贝构造函数创建一个string时,让新对象引用原对象,引用计数+1。

当通过赋值操作对一个string进行操作时,原对象引用计数-1,新对象引用计数+1。若原对象引用计数为负,则释放。当前对象引用新对象。

当通过[]操作符对string进行写操作时,对象引用计数-1。new一个新的字符串数组,用来进行写操作。

析构函数需要根据引用计数来判断是否释放资源。

 

#include <stdio.h>
#include <string>

#define _CRTDBG_MAP_ALLOC
#include "stdlib.h"
#include "crtdbg.h"

// Copy On Write string
class COW
{
public:
	COW(const char *c);
	COW(const COW &c);
	COW& operator = (const COW &c);
	char& operator[](int i);
	~COW();
	const char* c_str() const;
private:
	char *p;
};

// Constructor
COW::COW(const char *c)
{
	if(NULL != c)
	{
		int len = strlen(c);

		// Use p[len+1] to store the counter.
		p = new char[len + 2];
		strcpy(p, c);

		// Set the counter to 0;
		p[len+1] = 0;
	}
}

// Destructor
COW::~COW()
{
	int len = strlen(p);

	// Decrease the counter.
	p[len+1]--;

	// If there is no object referring to this string, release it.
	if((p[len+1] < 0) && (p != NULL))
		delete[] p;
}

// Copy constructor
COW::COW(const COW &c)
{
	int len = strlen(c.p);

	// This new object is referring to the source string.
	this->p = c.p;

	// Increase the counter.
	this->p[len+1]++;
}

// Assignment operator
COW& COW::operator = (const COW &c)
{
	if(this != &c)
	{
		int len1 = strlen(this->p);
		int len2 = strlen(c.p);

		// Decrease the current object's reference counter.
		this->p[len1+1]--;

		// Increase the source object's reference counter.
		c.p[len2+1]++;

		// If there is no object referring to the original
		// string, release it.
		if(this->p[len1+1] < 0)
			delete[] this->p;

		// This new object is referring to the source string.
		this->p = c.p;
	}

	return *this;
}

char& COW::operator[](int i)
{
	static char s = NULL;
	if(p == NULL)
		return s;

	int len = strlen(p);
	if(i>len)
		return s;

	// If the current object is not the only one who is referring to the string,
	// create a new string to write(copy on write).
	if(p[len+1] > 0)
	{
		p[len+1]--;
		char *t = new char[len+2];
		strcpy(t, p);
		t[len+1] = 0;
		p = t;
	}

	return p[i];
}

// Get the C style string
const char* COW::c_str() const
{
	return p;
}

int main()
{
	// Check for memory leak.
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	COW c1("adsfasdf");
	COW c2(c1);
	COW c3 = c2;	// still copy construct.

	c3 = c2;		// assignment

	// c1, c2 and c3 are referring to the same string.
	printf("%x, %x, %x\n", c1.c_str(), c2.c_str(), c3.c_str());

	// Since c1 is being written, c2 and c3 are refering to the original
	// string, c1 will refer to a new string.
	c1[3] = '1';
	printf("%x, %x, %x\n", c1.c_str(), c2.c_str(), c3.c_str());

	// Since c3 is being written, c2 is refering to the original
	// string, c3 will refer to a new string.
	c3[3] = '1';
	printf("%x, %x, %x\n", c1.c_str(), c2.c_str(), c3.c_str());

	// Since c2 is the only one who is referring to the original
	// string, the write operation will be on the original string.
	c2[3] = '1';
	printf("%x, %x, %x\n", c1.c_str(), c2.c_str(), c3.c_str());

    return 0;
}

 

Copy On Write的好处是当对象通过已有对象构造时,多个对象引用到同一资源。若针对对象的操作都是读操作,可以节省空间。当进行写操作时,再创建新的资源。

Copy On Write也会带来一些问题。若在DLL中返回的对象与当前单元中的对象引用到同一资源,且该资源在DLL中创建。在DLL被动态卸载后,资源被释放。当前单元中的对象所引用的资源则非法。

posted on 2011-09-13 21:54  playerken  阅读(1779)  评论(0编辑  收藏  举报