前言:

  说是漫游,其实就是扯,一点一点的扯。

  话说之前参加华为的德州扑克比赛,我用C++解析消息的时候碰到一个小问题,就是定长收消息的时候出错,在Linux下调了很久很久,终于发现,sizeof(string)不是string的size,而是string类型的大小。当然,用string.size()就可以轻松解决了,而作品也在昨晚提交了,不过,交的是python的。我的C++程序,送给了两支队伍,让他们去参赛,可惜,白眼狼。

  既然有闲暇时间了,那么就要深究一下,sizeof(string)是个什么鬼,我从代码测试开始,查阅到容器的几个属性的不同,再定位到了string的几种实现,终于明白了sizeo(string)f的大小,又对引用计数产生量兴趣。下文将按照这个路线,简单的阐述一下过程与实现。

一、sizeof(string)的两个值

  先上测试代码:

  

#include <string>
#include <iostream> int main()
{
std::string first = "abcde";
std::string second = first;
std::string c; std::cout << "sizeof result of each str " << std::endl;
std::cout << "first:" << sizeof(first) << std::endl;
std::cout << "second:" << sizeof(second) << std::endl;
std::cout << "c:" << sizeof(c) << std::endl; return ;
}

  本代码是测试三种不同形式的string的大小,运行结果如下:

  

  全是28,是char*的7倍。此结果是在windows下,vs2013跑的结果。而在Linux下,g++4.6的结果是4。

  那么问题来了为什么first,second,c的sizeof都是一样大小,而又为什么在不同编译器下,值不同呢?

  首先,回答为什么first,second,c的sizeof都是一样大小。

  1、什么是sizeof。

    sizeof是一个关键字,返回对象在内存中的大小,例如sizeof(char*) = 4. 那么sizeof(string)返回的当然是string这个变量在内存(栈)中的大小了。不过为什么对于不同string,在同一平台下返回的值都是相同的。

  2、string在vs2013中实现的猜测。

    首先,string是一种容器,那么它有这容器的函数size()与capacity(),其中,size()是容器对象实际存储的对象个数,而capacity()返回的是容器对象总共可以存储的值。那么,既然sizeof(string)的返回值是char * 的7倍,那么是不是value存在一个动态内存中,而size(),capacity()等在一块呢,加上点什么,然后凑够了7倍的char *?

    经过查阅资料发现,有一种string的实现是这样的:string对象指针大小的7倍,包含:一个分配子,默认大小为15的存实际字符的value,一个size(),一个capacity(),总共:4+15+1+4+4 = 28;其中1代表字符串预留的''0',方便c_str()的实现.正好是char*的7倍。示意图如下:

                                                      

    也就是说,当字符串的有效字符少于15时,创建一个新的对象,不会导致动态内存分配,那么当字符串长度大于15呢?则在Value部分挤出一块作为指向动态内存的指针,动态内存中存实际的字符,示意图如下:

                                              

   通过对有效字符小于15的string调用capacity()发现,确实是15,而大于15的,则为会大于等于实际字符,这个与容器的内存分配策略有关。属于预分配空间的一种。至此,解决了,为什么first,second,c的sizeof返回值在同一平台下一样的问题。

   其次,解释为什么g++与vs2013下结果不同。

   原因很简单了,string在不同平台下可能存在不同的实现。既然Linux的g++下返回值是4,那么猜测它就一个指针,然后指针指向了具体的结构。经过查资料发现,至少有两种string的实现,在sizeof下都会返回4.

    1、第一种可能的实现

    这种实现是一个指针指向一个结构,然后这个结构一个地方指向有效字符,示意图如下:

                                            

    Other是一些与同步相关的数据。创建该实现的对象时,至少会导致两次内存动态分配。

    2、第二种可能的实现

    该实现比上一种直接一些,示意图如下:

                                    

     创建该对象时,只会引发一次动态内存分配。

二、“拖延症”与引用计数

细心的读者可能发现在最后两种视线中有个RefCnt。这个是引用计数,那么它是做什么用的呢?

  1、写时复制

  在后两种实现中,你当用出如下语句时 :

      

string s = s1;

s 与 s1只是pointer不同,其余的都是共享的,这样可以尽量减少构造函数与析构函数的调用。当其中一个发生变化时,被写入新值时,才会真正的创建一个实实在在的对象。Linux中的fwrite等IO都拖延症的思想,fork也是如此,这样尽量减少内存分配,构造与析构。

  2、引用计数

  由于多个指针共享同一动态内存,只有当RefCnt = 0时,才会析构内存,这样保证每个共享的字符串都是能正常工作的。Linux中文件描述符就是这样的。

参考:

  Effective STL

  

从sizeof(string)到引用计数的漫游的更多相关文章

  1. Delphi结构中使用String时遇到的内存泄露问题(没有利用String的引用计数自动销毁字符串的功能)

    先定义一个结构: TUserInfo = record  UserID: Integer; // 用户编号  UserName: string; // 用户名end; 然后编写如下代码: proced ...

  2. 【转载】C++应用引用计数技术

    原帖:http://www.cnblogs.com/chain2012/archive/2010/11/12/1875578.html 因为Windows的内核对象也运用了引用计数,所以稍作了解并非无 ...

  3. String 类的实现(3)引用计数实现String类

    我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N的值主要用于析 ...

  4. String 类的实现(2)引用计数与写时拷贝

    1.引用计数 我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N ...

  5. (转)C++——std::string类的引用计数

    1.概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功课,于是你把自己关在房间里 ...

  6. 使用引用计数和copy-on_write实现String类

    本文写于2017-01-18,从老账号迁移到本账号,原文地址:https://www.cnblogs.com/huangweiyang/p/6295420.html 这算是我开始复习的内容吧,关于st ...

  7. swift学习笔记5——其它部分(自动引用计数、错误处理、泛型...)

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  8. C++ 引用计数技术及智能指针的简单实现

    一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...

  9. swif-自动引用计数

    import UIKit /* class Person { let name: String //强引用 init(name: String) { self.name = name print(&q ...

随机推荐

  1. 隐马尔可夫模型HMM

    隐马尔可夫模型HMM的探究 1 HMM基本概念1.1 定义1.2 观测序列生成过程1.3 HMM的三个问题2 概率计算算法2.1 直接计算算法2.2 前向算法forward algorithm2.3 ...

  2. 【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set

    题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2].一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2 ...

  3. POJ——2449 Remmarguts' Date

    Description "Good man never makes girls wait or breaks an appointment!" said the mandarin ...

  4. BZOJ1513 [POI2006]Tet-Tetris 3D 【二维线段树】

    题目链接 BZOJ1513 题解 真正地理解了一波线段树标记永久化的姿势 每个节点维护两个值\(v\)和\(tag\) \(v\)代表儿子中的最值 \(tag\)代表未下传的最值 显然节点的区间大于等 ...

  5. 假的kd-tree小结

    至今还不是很体会kd-tree这种东西,只不过体会了一种解决某些枚举问题的方法,就是当我们有一群元素,我们要到一个答案,答案在这些元素中的某个或某几个中,我们就会枚举他们,然而我们发现这样做十分低效, ...

  6. Linux产生背景

    By francis_hao Oct 26,2016 很久很久以前,大概在1965年左右,由贝尔实验室(Bell).麻省理工学院(MIT)及通用电气公司(GE)共同发起了一个叫做Multics的项目, ...

  7. [bzoj 2733]启发式合并权值线段树

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 平衡树待学习.从一个博客学到了合并权值线段树的姿势:http://blog.csdn ...

  8. Spring MVC框架下 从后台读取数据库并显示在前台页面【笔记自用 不推荐作为参考】

    1.书写jsp页面  people.jsp 1.设计显示格式以及内容显示 2.设计显示内容的范围 2.书写entity实体类 PeopleFormMap.java 书写传入的参数主要包括 要引用的数据 ...

  9. 在DirectX11下用Stencil Buffer绘制可视化Depth Complexity

    这是一道在<Introduction to 3D Game Programming with DirectX 11>上的练习题. 要求把某个像素点上的Depth Complexity(深度 ...

  10. [bzoj1031][JSOI2007]字符加密Cipher——后缀数组

    Brief Description 给定一个长度为n的字符串,你需要对其进行加密. 把字符串围成一个环 显然从任意一个位置开始都可以有一个长度为n的串 把产生的n个串按字典序排序,把这n个串的最后一个 ...