zoukankan      html  css  js  c++  java
  • 从sizeof(string)到引用计数的漫游

    前言:

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

      话说之前参加华为的德州扑克比赛,我用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 0;
    }

      本代码是测试三种不同形式的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

       

  • 相关阅读:
    URAL 2067 Friends and Berries (推理,数学)
    URAL 2070 Interesting Numbers (找规律)
    URAL 2073 Log Files (模拟)
    URAL 2069 Hard Rock (最短路)
    URAL 2068 Game of Nuts (博弈)
    URAL 2066 Simple Expression (水题,暴力)
    URAL 2065 Different Sums (找规律)
    UVa 1640 The Counting Problem (数学,区间计数)
    UVa 1630 Folding (区间DP)
    UVa 1629 Cake slicing (记忆化搜索)
  • 原文地址:https://www.cnblogs.com/tntboom/p/4550271.html
Copyright © 2011-2022 走看看