C++的std::string的“读时也拷贝”技术!
C++的std::string的读时也拷贝技术!
嘿嘿,你没有看错,我也没有写错,是读时也拷贝技术。什么?我的错,你之前听说写过时才拷贝,嗯,不错的确有这门技术,英文是Copy On Write,简写就是COW,非常’牛’!那么我们就来看看这个’牛’技术的效果吧。
我们先编写一段程序
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <string>#include <iostream>#include <sys/time.h>static long getcurrenttick(){ long tick ; struct timeval time_val; gettimeofday(&time_val , NULL); tick = time_val.tv_sec * 1000 + time_val.tv_usec / 1000 ; return tick;}int main( ){ string the_base(1024 * 1024 * 10, 'x'); long begin = getcurrenttick(); for( int i = 0 ;i< 100 ;++i ) { string the_copy = the_base ; } fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );} |
嗯,一个非常大的字符串,有10M字节的x,并且执行了100此拷贝。编译执行它,非常快,在我的虚拟机甚至不要1个毫秒。
现在我们来对这个string加点料!
|
1
2
3
4
5
6
7
8
9
|
int main(void) { string the_base(1024 * 1024 * 10, 'x'); long begin = getcurrenttick(); for (int i = 0; i < 100; i++) { string the_copy = the_base; the_copy[0] = 'y'; } fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );} |
现在我们再编译并执行这断程序,居然需要4~5秒!哇!非常美妙的写时才拷贝技术,性能和功能的完美统一。
我们再来看看另外一种情况!
|
1
2
3
4
|
string original = "hello";char & ref = original[0];string clone = original;ref = 'y'; |
我们生成了一个string,并保留了它首字符的引用,然后复制这个string,修改string中的首字符。因为写操作只是直接的修改了内存中的指定位置,这个string就根本不能感知到有写发生,如果写时才拷贝是不成熟的,那么我们将同时会修改original和clone两个string。那岂不是灾难性的结果?幸好上述问题不会发生。clone的值肯定是没有被修改的。看来COW就是非常的牛!
以上都证明了我们的COW技术非常牛!
有太阳就有黑暗,这句说是不是有点耳熟?
|
1
2
3
4
5
6
7
8
9
|
int main(void) { string the_base(1024 * 1024 * 10, 'x'); fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] ); long begin = getcurrenttick(); for (int i = 0; i < 100; i++) { string the_copy = the_base; } fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );} |
啊,居然也是4~5秒!你可能在想,我只是做了一个读,没有写嘛,这到底是怎么回事?难道还有读时也拷贝的技术!。
不错,为了避免了你通过[]操作符获取string内部指针而直接修改字符串的内容,在你使用了the_base[0]后,这个字符串的写时才拷贝技术就失效了。
C++标准的确就是这样的,C++标准认为,当你通过迭代器或[]获取到string的内部地址的时候,string并不知道你将是要读还是要写。这是它无法确定,为此,当你获取到内部引用后,为了避免不能捕获你的写操作,它在此时废止了写时才拷贝技术!
这样看来我们在使用COW的时候,一定要注意,如果你不需要对string的内部进行修改,那你就千万不要使用通过[]操作符和迭代器去获取字符串的内部地址引用,如果你一定要这么做,那么你就必须要付出代价。当然,string还提供了一些使迭代器和引用失效的方法。比如说push_back,等, 你在使用[]之后再使用迭代器之后,引用就有可能失效了。那么你又回到了COW的世界!比如下面的一个例子
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int main( ){ struct timeval time_val; string the_base(1024 * 1024 * 10, 'x'); long begin = 0 ; fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] ); the_base.push_back('y'); begin = getcurrenttick(); for( int i = 0 ;i< 100 ;++i ) { string the_copy = the_base ; } fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );} |
一切又恢复了正常!如果对[]返回引用进行了操作又会发生情况呢,有兴趣的朋友可以试试!结果非常令人惊讶。
另外:上述例子是在linux环境下编译的,使用STL是GNU的STL。windows上我用的是vs2003,但是非常明显vs2003一点都不支持COW。
这篇文章出自http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/ 这里,我使用了它的例子。但是我重新自己组织了内容。
编写这篇文章的同时,我还参考了耗子的《标准C++类string的Copy-On-Write技术》一文
C++的std::string的“读时也拷贝”技术!的更多相关文章
- 标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)
标准C++类std::string的内存共享,值得体会: 详见大牛:https://www.douban.com/group/topic/19621165/ 顾名思义,内存共享,就是两个乃至更多的对象 ...
- std::string的拷贝赋值研究
说明:以下涉及的std::string的源代码摘自4.8.2版本.结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址. // std::string类定义type ...
- (转)C++——std::string类的引用计数
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++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功 ...
- 使用 istreambuf_iterator 读取文件内容,赋值给 std::string
需要一个一个字符输入时考虑使用istreambuf_iterator 假设我们要把一个文本文件拷贝到一个字符串对象中.似乎可以用一种很有道理的方法完成: ifstream inputFile(&quo ...
- 标准C++类std::string的内存共享和Copy-On-Write技术
标准C++类std::string的 内存共享和Copy-On-Write技术 陈皓 1. 概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是 ...
- Folly解读(零) Fbstring—— 一个完美替代std::string的库
string 常见的三种实现方式 eager copy COW SSO Fbstring 介绍 Storage strategies Implementation highlights Benchma ...
- std::string类详解
之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必 担心内存是否足够.字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至 ...
随机推荐
- 微软BI 之SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表
基于数据仓库上的 SSRS 报表展示,一般可以直接通过 SQL 查询,存储过程,视图或者表等多种方式将数据加载并呈现在报表中.但是如果是基于 Cube 多维数据集的数据查询,就不能再使用 SQL 的语 ...
- JAVA设计模式——第 3 章 单例模式【Singleton Pattern】(转)
这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如此的有人缘,单例就是单一.独苗的意思,那什么是独一份呢?你的思维是独一份,除此之外还有什么不能山寨的呢?我们举个比较难复制的 ...
- 一个简单的C/S事例——JAVA-Socket
TalkClient.java import java.io.*; import java.net.*; public class TalkClient { public static void ma ...
- vim自动保存折叠
我经常使用的是手工折叠方式,就是 :set foldmethod=manual vim常用的折叠命令有: zf:折叠选定的行 zo:打开折叠 zR:打开所有折叠 zM:关闭所有折叠 zd:删除折叠 我 ...
- BIEE Demo(RPD创建 + 分析 +仪表盘 )
说明:此Demo步骤简略,详细Demo可以参照下面的 天善视频:BIEE 11G Rpd模型设计 天善视频:BIEE 11G 报表开发 Oracle BIEE (Business Intelligen ...
- jQuery操作radio、checkbox、select总结
1.radio:单选框 HTML代码: <input type="radio" name="radio" id="radio1" va ...
- XML文件生成C++代码(基于pugixml)
简述 在一个项目中需要用到XML的解析和生成,知乎上有人推荐rapidxml和pugixml等库.RapidXML一看库还比较大,就先研究一下pugixml了. 因为对解析XML的需求不大(都是一些很 ...
- Linux alias别名设置
alias命令用来设置指令的别名.我们可以使用该命令可以将一些较长的命令进行简化.使用alias时,用户必须使用单引号''将原来的命令引起来,防止特殊字符导致错误. 语法 alias(选项)(参数) ...
- word2vec训练中文模型
-- 这篇文章是一个学习.分析的博客 --- 1.准备数据与预处理 首先需要一份比较大的中文语料数据,可以考虑中文的维基百科(也可以试试搜狗的新闻语料库).中文维基百科的打包文件地址为 https: ...
- Screen2EXE录屏|录制视频
Screen2EXE是一款具有独到压缩算法的屏幕录制软件,它可以记录用户在屏幕上的每一步操作,包括鼠标轨迹,点击动作给予花环提示,然后保存为不需播放器即可观看的exe可执行文件. 在生成录制文件的大小 ...