C++: string copy-on-write

 https://blog.csdn.net/haoel/article/details/24058

https://www.cnblogs.com/promise6522/archive/2012/03/22/2412686.html

https://stackoverflow.com/questions/6245235/confusion-about-copy-on-write-and-shared-ptr

https://stackoverflow.com/questions/628938/what-is-copy-on-write

https://en.wikipedia.org/wiki/Copy-on-write

 

 

string COW

1、
string str1 = "hello world";

printf ("\tstr1's address: %x\n", str1.c_str() );

输出str1的地址

std::cout << "str1's address: " << std::hex << str1.c_str() << std::endl;
输出str1的内容


std::cout 的类型 std::ostream 的基类 std::basic_ostream 有一个这样的 operator<< 重载:basic_ostream& operator<<( const void* value );
这个重载可以输出指针的值(也就是地址)。----------------------------------------然而 std::basic_ostream 还有几个非成员 operator<< 重载:template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
const CharT* s );

template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
const char* s );

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
const char* s );

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
const signed char* s );

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
const unsigned char* s );
这些函数负责输出由 char* 或其他字符类型的指针表示的字符串。----------------------------------------所以由于 char* (或者 charT*)比 void* 更加特化(不知道特化是什么没关系,以后会学到),在 【cout << p】且 p 的类型是 char* 的情况下,负责输出字符串的 operator<< 重载会被调用。


2、原始版本


// string
class CMyString
{
public:
CMyString(const char* initValue = "");
~CMyString();
CMyString(const CMyString& rhs);
CMyString& operator= (const CMyString& rhs);

private:
char* data;
};


// String
CMyString::CMyString(const char* initValue)
{
if (NULL == initValue)
{
data = new char[1];
*data = '\0';
}
else
{
data = new char[strlen(initValue) + 1];
strcpy(data, initValue);
}
}


CMyString::~CMyString()
{
delete []data;
}


CMyString::CMyString(const CMyString& rhs)
{
data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
}


/*
CMyString& CMyString::operator= (const CMyString &rhs)
{
if (this != &rhs)
{
delete [] data;
data = NULL;

data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
}

return *this;
}
*/


CMyString& CMyString::operator= (const CMyString &rhs)
{
// 考虑异常安全
if (this != &rhs)
{
CMyString tmpString(rhs);

char* pTemp = tmpString.data;
tmpString.data = data;
data = pTemp;
}

return *this;
}

 

3、引用计数
创建一个带引用计数的String类并不困难,但需要注意一些细节,所以我们将略述这样一个类的大部分常用成员函数的实现。然而,在开始之前,认识到“我们需要一个地方来存储这个计数值”是很重要的。这个地方不能在String对象内部,因为需要的是每个String值一个引用计数值,而不是每个String对象一个引用计数。这意味着String值和引用计数间是一一对应的关系,所以我们将创建一个类来保存引用计数及其跟踪的值。我们叫这个类StringValue,又因为它唯一的用处就是帮助我们实现String类,所以我们将它嵌套在String类的私有区内。另外,为了便于Sting的所有成员函数读取其数据区,我们将StringValue申明为struct。需要知道的是:将一个struct内嵌在类的私有区内,能便于这个类的所有成员访问这个结构,但阻止了其它任何人对它的访问(当然,除了友元)。

基本设计是这样的:

class String
{
public:
String(const char* initValue = "");
~String();
String(const String& rhs);
String& operator= (const String& rhs);

const char& operator[] (int index) const;
char& operator[] (int index);

private:
struct StringValue
{
int refCount;
char *data;

StringValue(const char *initValue);
~StringValue();
};

StringValue* value;
};


String::StringValue::StringValue(const char *initValue)
: refCount(1)
{
data = new char[strlen(initValue) + 1];
strcpy(data, initValue);
}


String::StringValue::~StringValue()
{
delete [] data;
}


这是其所有的一切,很清楚,这不足以实现带引用计数的String类。一则,没有拷贝构造函数和赋值运算;二则,没有提供对refCount的操作。别担心,少掉的功能将由String类提供。StringValue的主要目的是提供一个空间将一个特别的值和共享此值的对象的数目联系起来。StringValue给了我们这个,这就足够了。


String::String(const char* initValue)
: value(new StringValue(initValue))
{

}


String::~String()
{
if (0 == --value->refCount)
{
delete value;
}
}


String::String(const String& rhs)
: value(rhs.value)
{
++value->refCount;
}


String& String::operator= (const String &rhs)
{
if (value == rhs.value)
{
return *this;
}

if (0 == --value->refCount)
{
delete value;
}

value = rhs.value;
++value->refCount;

return *this;
}


const char& String::operator[] (int index) const
{
return value->data[index];
}


char& String::operator[] (int index)
{
// if we're sharing a value with other String objects,
// break off a separate copy of the value for ourselves
if (value->refCount > 1)
{
--value->refCount; // decrement current value's refCount
value = new StringValue(value->data); // make a copy of the value for ourselves
}

// return a reference to a character inside our
// unshared StringValue object
return value->data[index];
}

 

我们希望以不同的方式处理读和写。简单的读操作,可以用与const的operator[]类似的方式实现,而写操作必须用完全不同的方式来实现。
当我们修改一个String对象的值时,必须小心防止修改了与它共享相同StringValue对象的其它String对象的值。不幸的是,C++编译器没有办法告诉我们一个特定的operator[]是用作读的还是写的,所以我们必须保守地假设“所有”调用非const operator[]的行为都是为了写操作。(Proxy类可以帮助我们区分读还是写,见Item M30。)
为了安全地实现非const的operator[],我们必须确保没有其它String对象在共享这个可能被修改的StringValue对象。简而言之,当我们返回StringValue对象中的一个字符的引用时,必须确保这个StringValue的引用计数是1。

编译器根据调用成员函数的对象的const属性来选择此成员函数的const和非const版本,而不考虑调用时的环境。

 

posted @ 2017-09-25 15:54  朔方  阅读(578)  评论(0编辑  收藏  举报