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版本,而不考虑调用时的环境。

C++: string copy-on-write的更多相关文章

  1. string::copy

    size_t copy (char* s, size_t len, size_t pos = 0) const;功能:把string的pos位置开始的len字节copy到s注意:s的最后要手动添加字符 ...

  2. std::string::copy函数

    size_t copy (char* s, size_t len, size_t pos = 0) const;

  3. string中c_str()、data()、copy(p,n)函数的用法

    标准库的string类提供了3个成员函数来从一个string得到c类型的字符数组:c_str().data().copy(p,n). 1. c_str():生成一个const char*指针,指向以空 ...

  4. C#入门篇6-3:字符串操作 string的ToString() Split()和Copy()方法

    //ToString()方法 public static void OutPut() { //字符型转换 转为字符串 Console.WriteLine(.ToString("n" ...

  5. iOS 浅谈:深.浅拷贝与copy.strong

    深.浅拷贝 copy mutableCopy NSString NSString *string = @"汉斯哈哈哈"; // 没有产生新对象 NSString *copyStri ...

  6. Copy 与MutableCopy的区别

    NSString *string = @"origion"; NSString *stringCopy = [string copy]; NSMutableString *stri ...

  7. Objective -C学习笔记 之copy(复制)

    //自定义类对象实现copy需要遵守copy协议(否则程序崩溃),实现必须实现的协议方法,里面的代码就决定了你的copy是深是浅 #import <Foundation/Foundation.h ...

  8. retain copy(浅复制) mutablecopy (深复制)

    http://blog.csdn.net/xdrt81y/article/details/24331103   口诀: 1浅3深   NSArray copy (浅) 返回NSArray NSArra ...

  9. 转载一篇关于ios copy的文章

    由于原文创作时间较早,一些内容不实用了,我对其进行了加工,去掉了一部分内容,添加了一点注释. 原文连接 http://www.cnblogs.com/ydhliphonedev/archive/201 ...

  10. iOS - OC Copy 拷贝

    前言 copy:需要先实现 NSCopying 协议,创建的是不可变副本. mutableCopy:需要实现 NSMutableCopying 协议,创建的是可变副本. 浅拷贝:指针拷贝,源对象和副本 ...

随机推荐

  1. 网络编程(四)——基于udp协议的套接字socket、socketserver模块的使用

    基于udp协议的套接字.socketserver模块 一.UDP协议(数据报协议) 1.何为udp协议 不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包. ...

  2. 高程(三)--- Date

    Date类型使用UTC(国际协调时间)1970年1月1日0时0分始到现在的毫秒数来保存日期的. 所以当我们知道毫秒数时,还需要通过计算才能获取年月日时分秒. 一.获取时间对象 Date提供了2个方法: ...

  3. 38-Ubuntu-用户管理-03-usermod指定用户登录shell

    简记: 所谓shell就是可以输入终端命令的窗口,shell是一个软件. 1.Ubuntu终端shell介绍 summmer@summmer-virtual-machine:~/桌面$ summmer ...

  4. python项目部署

    WSGI简介 Web框架和Wen服务器之间需要进行通信,如果在设计时它们之间无法相互匹配,那么对框架的选择就会限制对Web服务器的选择,这显然是不合理的.这时候需要设计一套双方都遵守的接口.WSGI是 ...

  5. 推荐20个让你学习并精通CSS的网站

    1. A List Apart CSS Topics A List Apart,学习网页设计和最佳实践的首选网站.这个网站从1999年就开始发表关于CSS的文章,其中大部分文章都是面向那些更注重符合标 ...

  6. 笔记-Linux包管理命令

    一.apt, apt-get, dpkg命令 apt-get是一条linux命令,适用于deb包管理式的操作系统,主要用于自动从互联网的软件仓库中搜索.安装.升级.卸载软件或操作系统.使用apt-ge ...

  7. 基于nginx结合openssl实现https

    [root@localhost ~]#systemctl stop firewalld[root@localhost ~]#setenforce 0[root@localhost ~]#iptable ...

  8. leetcood学习笔记-226- 翻转二叉树

    题目描述: 第一次提交: class Solution(object): def invertTree(self, root): """ :type root: Tree ...

  9. VS 2015 Download

    企业版:http://download.microsoft.com/download/B/8/F/B8F1470D-2396-4E7A-83F5-AC09154EB925/vs2015.ent_chs ...

  10. px2rem-loader(Vue:将px转化为rem,适配移动端)

    转载:https://www.cnblogs.com/WQLong/p/7798822.html 1.下载lib-flexible 使用的是vue-cli+webpack,通过npm来安装的 npm ...