标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)
标准C++类std::string的内存共享,值得体会:
详见大牛:https://www.douban.com/group/topic/19621165/
顾名思义,内存共享,就是两个乃至更多的对象,共同使用一块内存;
1.关于string的内存共享问题:
通常,string类中必有一个私有成员,其是一个char*,用户记录从堆上分配内存的地址,其在构造时分配内存,在析构时释放内存。
因为是从堆上分配内存,所以string类在维护这块内存上是格外小心的,string类在返回这块内存地址时,只返回const
char*,也就是只读的,
如果你要写,也只能通过string提供的方法进行数据的改写。
#include<iostream>
#include<string>
#include<cstdio>
using namespace std; main()
{
string str1 = "hello world";
string str2 = str1;
string str3 = str2; printf ("内存共享:\n");
printf ("\tstr1 的地址: %x\n", (unsigned int)str1.c_str() );
printf ("\tstr2 的地址: %x\n", (unsigned int)str2.c_str() );
printf ("\tstr3 的地址: %x\n", (unsigned int)str3.c_str() ); return 0;
}
如上例子中,str1,str2,str3共享同一块内存,如图:
基本就是内存string类内存共享的最底层展现了,既然内存是一样的了,如果需要改写某个对象怎么办?由此引出写时拷贝Copy-On-Write
2.关于Copy-On-Write(原理)
顾名思义,写的时候在拷贝,(读的时候就不用了,哈哈)
还是以上边的例子为例:
#include<iostream>
#include<string>
#include<cstdio>
using namespace std; main()
{
string str1 = "hello world";
string str2 = str1;
string str3 = str2; printf ("内存共享:\n");
printf ("\tstr1 的地址: %x\n", (unsigned int)str1.c_str() );
printf ("\tstr2 的地址: %x\n", (unsigned int)str2.c_str() );
printf ("\tstr3 的地址: %x\n", (unsigned int)str3.c_str() ); str3[1]='a';
str2[1]='w';
str1[1]='q'; printf ("通过写时拷贝之后:\n");
printf ("\tstr1 的地址: %x\n", (unsigned int)str1.c_str() );
printf ("\tstr2 的地址: %x\n", (unsigned int)str2.c_str() );
printf ("\tstr3 的地址: %x\n", (unsigned int)str3.c_str() ); return 0;
} //输出结果:
内存共享:
str1 的地址: 83f9017
str2 的地址: 83f9017
str3 的地址: 83f9017
通过写时拷贝之后:
str1 的地址: 83f9017
str2 的地址: 83f9054
str3 的地址: 83f9034
很明显可以看出来,一开始,str1,str2,str3共享同一块内存,地址都是一样的;
当开始修改是这些内存是,先不说如何实现,先表征是如何写时拷贝的,看图,咱还是看图:
图中依然说明了str3的内容修改是怎么回事,str2的内容修改,也是同样的道理,重新给str2在堆上开辟空间,原空间只是str1一个人用,修改最后一个str1的内容时,
当然就不用在和前两种一样啦,因为,这个时候,原空间只有str1一个人用,这个时候,对此空间操作,没有任何问题。都写都可以;
写时拷贝在此例中的体现,主要是str2,和str3内容的修改;但是有没有发现,我每次开辟空间的同时,会在新开辟的空间开头多分配一个空间,存放的是count;
原因就和写时拷贝的具体操作有关了:
3.写时拷贝(Copy-On-Write)的实现:
Copy-On-Write使用了“引用计数”,有一个变量count来计数,而且计数就放在没开辟一段空间的开头几个字节。
当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加,
当有类析构时,这个计数会减一,直到最后一个类析构时,此时的count为1或是0,此时,程序才会真正的Free这块从堆上分配的内存。
下面是我写的一个简单的例子:
#include<iostream>
using namespace std; class String
{
public:
String(const char* str)
//初始时字符创有一个\0外加4个字节的引用计数空间
:_str(new char[strlen(str)+5])
{
(*((int*)_str)) = 1;//申请的空间赋值为1
_str += 4; //让_str还是指向字符创的第一个字符
//而不是引用计数的头上
strcpy(_str,str);
} String(const String& s)
:_str(s._str)
{
(*(((int*)_str) - 1)) += 1;
} String& operator=(const String& s)
{
if(_str != s._str)
{
if(*(((int*)_str) - 1) == 0)
{
delete[] (_str-4);
}
_str = s._str;
*(((int*)_str) - 1) += 1;
}
return *this; }
~String()
{
if(*(((int*)_str) - 1) == 0)
{
_str -= 4;
delete[] _str;
}
}
private:
char *_str;
}; void Test()
{
String s1("11111111111111111111111111");
String s2(s1);
} int main()
{
Test();
return 0;
}
在内存开头开辟引用计数空间;
到此,string类的内存共享和写时拷贝,就算是告一段落了,个人拙见,跪求赐教!
标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)的更多相关文章
- 标准C++类std::string的内存共享和Copy-On-Write...
标准C++类std::string的 内存共享和Copy-On-Write技术 陈皓 1. 概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否 ...
- 标准C++类std::string的内存共享和Copy-On-Write技术
标准C++类std::string的 内存共享和Copy-On-Write技术 陈皓 1. 概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是 ...
- 【转】标准C++类std::string的内存共享和Copy-On-Write技术
1. 概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功 ...
- String类的实现(4)写时拷贝浅析
由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝.这也是一种 ...
- String 类的实现(2)引用计数与写时拷贝
1.引用计数 我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N ...
- String写时拷贝实现
头文件部分 1 /* 版权信息:狼 文件名称:String.h 文件标识: 摘 要:对于上版本简易的String进行优化跟进. 改进 1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需 ...
- VisualStudio下std::string的内存布局
主要成员 union _Bxty { // storage for small buffer or pointer to larger one _Elem _Buf[_BUF_SIZE]; _Elem ...
- 转C++之stl::string写时拷贝导致的问题
前几天在开发某些数据结构到文件的 Dump 和 Load 功能的时候, 遇到的一个 bug . [问题复现] 问题主要出在 Load 过程中,从文件读取数据的时候, 直接使用 fread 的去操作 s ...
- C++中,如何在标准库的std::string和常用库(Qt,VC等)的QString之间进行选择?
假设一个场景:在写GUI程序的时候,如果GUI库和STL都提供了某个功能(比如容器字符串),应该如何在两个库之间选择? 做法是分层,比如分为frontend+core.开发core的时候只用STL,保 ...
随机推荐
- 通过分析HSL/HSB获取图片主色调
这两天稍微研究了一下颜色的HSL/HSB值,主要因为写程序想要实现通过一张图片拿到图片中的最突出的颜色值(类似Groove Music中播放栏背景就是从专辑封面中取出主色调,还有Windows 10任 ...
- luogu【P3387】【模板】缩点
原题入口 PS:这个题数据是由Hany01大大出出来的 %%% 这个题显然是一道强联通+DAGdp的题 (题目背景有= =) 缩点的原因就是:不缩会一直在一个地方绕圈圈 而且不能进行后面的DAPdp ...
- LCT维护子树信息(BZOJ4530:[BJOI2014]大融合)
题面 没有权限号的可以去LOJ Sol 大家都知道,\(LCT\)上有许多实边和虚边 实边就是每棵\(Splay\)上的既认父亲又认儿子的边 虚边就是\(Splay\)和\(Splay\)之间只认父亲 ...
- 机器学习-kNN
基于Peter Harrington所著<Machine Learning in Action> kNN,即k-NearestNeighbor算法,是一种最简单的分类算法,拿这个当机器学习 ...
- 24时区,GMT,UTC,DST,CST时间详解
全球24个时区的划分 相较于两地时间表,可以显示世界各时区时间和地名的世界时区表(World Time),就显得精密与复杂多了,通常世界时区表的表盘上会标示着全球24个时区的城市名称,但究竟 ...
- python 常用方法
在python的类中,除了常规的一些自定义函数调用之外还有一些内置函数或方法,大多数情况下不会用到,但是为了更好的学习到python类的原理也需要对其有一定的了解,下面我们一起来看一下都具体包含了那些 ...
- mount挂接命令使用
挂接 操作系统 1.-t vfstype 指定文件系统的类型,通常不必指定.mount 会自动选择正确的类型.常用类型有: 光盘或光盘镜像:iso9660 DOS fat16文件系统:msdos Wi ...
- java中数组复制的两种方式
在java中数组复制有两种方式: 一:System.arraycopy(原数组,开始copy的下标,存放copy内容的数组,开始存放的下标,需要copy的长度); 这个方法需要先创建一个空的存放cop ...
- c++ 指针总结 函数参数指针调用和堆栈内存的分配原理
c++中的char指针 这个char指针很有意思,char指针通常有两种初始化形式.一个是使用char数组初始化,一个是使用char变量初始化. c++当中使用双引号括起来的字符串起始已经被编译器初始 ...
- iOS 神秘而又强大的传感器系统 (附demo)
iOS中的各种传感器: 随着科技的发展,机器感知人的行为!Goole的无人驾驶汽车到李彦宏的无人驾汽车,都带入了各种计算及传感. 为了研究自然现象和制造劳动工具,人类必须了解外界的各类信息.了解外界信 ...