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被动态卸载后,资源被释放。当前单元中的对象所引用的资源则非法。
浙公网安备 33010602011771号