标准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提供的方法进行数据的改写。

[cpp] view plaincopy 在CODE上查看代码片派生到我的代码片
  1. #include<iostream>  
  2. #include<string>  
  3. #include<cstdio>  
  4. using namespace std;  
  5.    
  6. main()  
  7. {  
  8.        string str1 = "hello world";  
  9.        string str2 = str1;  
  10.        string str3 = str2;  
  11.     
  12.        printf ("内存共享:\n");  
  13.        printf ("\tstr1 的地址: %x\n", (unsigned int)str1.c_str() );  
  14.        printf ("\tstr2 的地址: %x\n", (unsigned int)str2.c_str() );  
  15.        printf ("\tstr3 的地址: %x\n", (unsigned int)str3.c_str() );  
  16.   
  17.        return 0;  
  18. }  
如上例子中,str1,str2,str3共享同一块内存,如图:


基本就是内存string类内存共享的最底层展现了,既然内存是一样的了,如果需要改写某个对象怎么办?由此引出写时拷贝Copy-On-Write

2.关于Copy-On-Write(原理)

顾名思义,写的时候在拷贝,(读的时候就不用了,哈哈)

还是以上边的例子为例:

[cpp] view plaincopy 在CODE上查看代码片派生到我的代码片
  1. #include<iostream>  
  2. #include<string>  
  3. #include<cstdio>  
  4. using namespace std;  
  5.    
  6. main()  
  7. {  
  8.        string str1 = "hello world";  
  9.        string str2 = str1;  
  10.        string str3 = str2;  
  11.     
  12.        printf ("内存共享:\n");  
  13.        printf ("\tstr1 的地址: %x\n", (unsigned int)str1.c_str() );  
  14.        printf ("\tstr2 的地址: %x\n", (unsigned int)str2.c_str() );  
  15.        printf ("\tstr3 的地址: %x\n", (unsigned int)str3.c_str() );  
  16.   
  17.        str3[1]='a';  
  18.        str2[1]='w';  
  19.        str1[1]='q';  
  20.     
  21.        printf ("通过写时拷贝之后:\n");  
  22.        printf ("\tstr1 的地址: %x\n", (unsigned int)str1.c_str() );  
  23.        printf ("\tstr2 的地址: %x\n", (unsigned int)str2.c_str() );  
  24.        printf ("\tstr3 的地址: %x\n", (unsigned int)str3.c_str() );  
  25.   
  26.        return 0;  
  27. }  
  28.   
  29. //输出结果:  
  30. 内存共享:  
  31.   str1 的地址: 83f9017  
  32.   str2 的地址: 83f9017  
  33.   str3 的地址: 83f9017  
  34. 通过写时拷贝之后:  
  35.   str1 的地址: 83f9017  
  36.   str2 的地址: 83f9054  
  37.   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这块从堆上分配的内存。 

下面是我写的一个简单的例子:

[cpp] view plaincopy 在CODE上查看代码片派生到我的代码片
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. class String  
  5. {  
  6. public:  
  7.     String(const char* str)  
  8.         //初始时字符创有一个\0外加4个字节的引用计数空间  
  9.         :_str(new char[strlen(str)+5])  
  10.     {  
  11.         (*((int*)_str)) = 1;//申请的空间赋值为1  
  12.         _str += 4; //让_str还是指向字符创的第一个字符  
  13.                    //而不是引用计数的头上  
  14.         strcpy(_str,str);  
  15.     }  
  16.   
  17.     String(const String& s)  
  18.         :_str(s._str)  
  19.     {  
  20.         (*(((int*)_str) - 1)) += 1;  
  21.     }  
  22.   
  23.     String& operator=(const String& s)  
  24.     {  
  25.         if(_str != s._str)  
  26.         {  
  27.             if(*(((int*)_str) - 1) == 0)  
  28.             {  
  29.                 delete[] (_str-4);  
  30.             }  
  31.             _str = s._str;  
  32.             *(((int*)_str) - 1) += 1;  
  33.         }  
  34.         return *this;  
  35.   
  36.   
  37.     }  
  38.     ~String()  
  39.     {  
  40.         if(*(((int*)_str) - 1) == 0)  
  41.         {  
  42.             _str -= 4;  
  43.             delete[] _str;  
  44.         }  
  45.     }  
  46. private:  
  47.     char *_str;  
  48. };  
  49.   
  50. void Test()  
  51. {  
  52.     String s1("11111111111111111111111111");  
  53.     String s2(s1);  
  54. }  
  55.   
  56. int main()  
  57. {  
  58.     Test();  
  59.     return 0;  
  60. }  
在内存开头开辟引用计数空间;

到此,string类的内存共享和写时拷贝,就算是告一段落了,个人拙见,跪求赐教!

转载于:https://www.cnblogs.com/zhoug2020/p/6542286.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/253156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决 | 此数据库文件跟当前sql server实例不兼容 sql server2008无法连接到(local)...

最近在搞ASP.NET&#xff0c;因实验室VS版本跟PC不一样可能&#xff0c;拷回来一打开就这样子&#xff1a; 眉头一皱的我打开我的古董SQL&#xff0c;自从用了MySQL就没碰它了我的锅。。。果然。。连接的时候。。。不慌&#xff0c;(win 10)打开控制面板\系统和安全\管理工具 -…

IP大时代下,网络枪机技术发展现状

来源&#xff1a;a&s《评测&选型》 作者&#xff1a;海康威视程玮 在视频监控行业内&#xff0c;有很多里程碑式的产品&#xff0c;我们可以从这些产品中了解视频监控的发展趋势。2000年左右&#xff0c;第一台DVR面世奠定了视频监控行业从CCTV走向数字化的基础&#x…

JVM调优总结(3):垃圾回收面临的问题

如何区分垃圾 上面说到的“引用计数”法&#xff0c;通过统计控制生成对象和删除对象时的引用数来判断。垃圾回收程序收集计数为0的对象即可。但是这种方法无法解决循环引用。所以&#xff0c;后来实现的垃圾判断算法中&#xff0c;都是从程序运行的根节点出发&#xff0c;遍历…

运放搭建主动滤波电路

主动低通滤波电路 R1R216K R3R4100K C1C20.01uF 放大倍数AvR4/(R3R4) Freq1KHz 主动高通滤波电路 C12*C20.02uF,C20.01uF R1R2110K 6dBLow-cutFreq100Hz

邮件实现详解(四)------JavaMail 发送(带图片和附件)和接收邮件

好了&#xff0c;进入这个系列教程最主要的步骤了&#xff0c;前面邮件的理论知识我们都了解了&#xff0c;那么这篇博客我们将用代码完成邮件的发送。这在实际项目中应用的非常广泛&#xff0c;比如注册需要发送邮件进行账号激活&#xff0c;再比如OA项目中利用邮件进行任务提…

运放搭建电压电流转换电路分析

如下图电路&#xff0c;电流可以转换成电压&#xff0c;电压也可以转换成电流&#xff1b; 根据虚断&#xff1a;(Vi–V1)/R2 (V1–V4)/R6 &#xff08;a&#xff09; 同理 (V3–V2)/R5V2/R4 &#xff08;b&#xff09; 根据虚短&#xff1a; V1V2 &#xff08;c&#xff09…

centos7装完chrome无法使用yum问题解决

2019独角兽企业重金招聘Python工程师标准>>> 续前文装好chrome后&#xff0c;yum居然用不了&#xff0c;提示错误“Basic XLib functionality test failed!” 呵呵。。。呵呵了.... 【题外话~个人真心觉得pythonseleniumchrome在linux环境下开发和使用 简直蛋疼无比…

实验二第二部分

第二部分 FTP协议分析 1. 两个同学一组&#xff0c;A和B。 2.A同学架设FTP服务器&#xff0c;并设置用户名和密码&#xff0c;例如gao / gao 3.B同学在机器中安装Wireshark&#xff0c;并将其打开&#xff1b;之后用用户名和密码登陆A同学的FTP服务器&#xff0c;并上传一张图片…

运放搭建的跟随电路作用与分析

电压跟随器&#xff0c;顾名思义就是输出电压与输入电压是相同的&#xff0c;就是说电压跟随器的电压放大倍数恒小于且接近1。 电压跟随器的显著特点就是&#xff0c;输入阻抗高&#xff0c;而输出阻抗低。 根据其显著特点&#xff0c;常见的作用如下&#xff1a; 1- 缓冲 在…

运放电压跟随电路应用

电压跟随器的显著特点&#xff1a;输入阻抗高&#xff0c;输出阻抗低。 如下所示为利用放大器搭建的电压跟随电路&#xff0c;方便测量电压大小&#xff1a; 此电路目的是测量电池电压&#xff0c;电池电压范围&#xff08;3~4.2V&#xff09;分压后最大电压为2.1V 属于3.3V电…

Mac与Phy组成原理的简单分析

Mac与Phy组成原理的简单分析 2011-12-28 15:30:43 //http://blog.chinaunix.net/uid-20528014-id-3050217.html 本文乃fireaxe原创&#xff0c;使用GPL发布&#xff0c;可以自由拷贝&#xff0c;转载。但转载请保持文档的完整性&#xff0c;并注明原作者及原链接。内容可任意使…

[BZOJ3994][SDOI2015]约数个数和

3994: [SDOI2015]约数个数和 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 1104 Solved: 762 [Submit][Status][Discuss]Description 设d(x)为x的约数个数&#xff0c;给定N、M&#xff0c;求 Input 输入文件包含多组测试数据。 第一行&#xff0c;一个整数T&#xff0…

大力智能台灯T6 结构拆解

近几年教育硬件产品层出不穷&#xff0c;教育硬件赛道布局时间较长的有网易、讯飞、步步高系等公司&#xff0c;2020年10月&#xff0c;字节跳动旗下大力教育经过两年多的调研和研发&#xff0c;高调推出首款智能硬件产品“大力智能作业台灯” T5。 上市一年取得不错的销售成绩…

第5章 IDA Pro

5.1 加载一个可执行文件 默认情况下IDA Pro的反汇编代码中不包含PE头或资源节&#xff0c;可以手动指定加载。 5.2 IDA Pro接口 5.2.1 反汇编窗口模式 二进制模式/图形模式&#xff1a; 图形模式&#xff1a;红色表示一个条件跳转没有被采用&#xff0c;绿色表示这个条件跳转被…

使用 typescript ,提升 vue 项目的开发体验(1)

此文已由作者张汉锐授权网易云社区发布。欢迎访问网易云社区&#xff0c;了解更多网易技术产品运营经验。前言&#xff1a;对于我们而言&#xff0c;typescript 更像一个工具官方指南从 vue2.5 之后&#xff0c;vue 对 ts 有更好的支持。根据官方文档&#xff0c;vue 结合 type…

Linux进程间通信——使用共享内存

//本文转载http://blog.csdn.net/ljianhui/article/details/10253345下面将讲解进程间通信的另一种方式&#xff0c;使用共享内存。一、什么是共享内存顾名思义&#xff0c;共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递…

不踩雷不将就 京东智能产品30天无忧退

剁手节已经来临&#xff0c;铺天盖地的促销信息让人应接不暇&#xff0c;恰好又是换季&#xff0c;确实需要买买买一波了。各种满减活动让人眼花缭乱&#xff0c;这波堪称全年最大力度的促销活动&#xff0c;令人是又喜又怕。倘若之前踩过雷的朋友&#xff0c;必然现在会谨慎许…

Linux进程间通信——使用信号量

//转自http://blog.csdn.net/ljianhui/article/details/10243617 这篇文章将讲述别一种进程间通信的机制——信号量。注意请不要把它与之前所说的信号混淆起来&#xff0c;信号与信号量是不同的两种事物。有关信号的更多内容&#xff0c;可以阅读我的另一篇文章&#xff1a;L…

麦克风阵列音频检查方法和标准

为确保产品能够符合算法要求&#xff0c;务必提前做好相关设计&#xff0c;尽量确保各项指标满足如下标准。 音频评测工作&#xff0c;主要集中在研发设计阶段&#xff1b;针对产品形态的不同&#xff0c;测试可分为裸板测试和整机测 试&#xff0c;下表为不同阶段需要测试的…

SVG格式图片转成HTML中SVG的Path路径

AI图标制作完成之后&#xff0c;保存的svg文件包含许多AI的信息&#xff0c;如果要在HTML中使用&#xff0c;我们需要在svg文件中提取/修改信息&#xff0c;重新保存。 1、在AI中已经完成图标&#xff0c;要保存SVG文件&#xff0c;点击“文件(File)”-“另存为(Save As)”&…