C++ string实现原理
C++程序员编码过程中经常会使用string(wstring)类,你是否思考过它的内部实现细节。比如这个类的迭代器是如何实现的?对象占多少字节的内存空间?内部有没有虚函数?内存是如何分配的?构造和析构的成本有多大?笔者综合这两天阅读的源代码及个人理解简要介绍之,错误的地方望读者指出。
首先看看string和wstring类的定义:
- typedef basic_string<char, char_traits<char>, allocator<char> > string;
- typedef basic_string<wchar_t, char_traits<wchar_t> allocator<wchar_t> > wstring;
从这个定义可以看出string和wstring分别是模板类basic_string对char和wchar_t的特化。
再看看basic_string类的继承关系(类方法未列出):
最顶层的类是_Container_base,它也是STL容器的基类,Debug下包含一个_Iterator_base*的成员,指向容器的最开始的元素,这样就能遍历容器了,并定义了了两个函数
- void _Orphan_all() const; // orphan all iterators
- void _Swap_all(_Container_base_secure&) const; // swaps all iterators
Release下_Container_base只是一个空的类。
_String_base类没有数据成员,只定义了异常处理的三个函数:
- static void _Xlen(); // report a length_error
- static void _Xran(); // report an out_of_range error
- static void _Xinvarg();
_String_val包含一个alloctor的对象,这个类也非常简单,除了构造函数没有定义其它函数。
上面三个基类都定义得很简单,而basic_string类的实现非常复杂。不过它的设计和大多数标准库一样,把复杂的功能分成几部分去实现,充分体现了模块的低耦合。
迭代器有关的操作交给_String_iterator类去实现,元素相关的操作交给char_traits类去实现,内存分配交给allocator类去实现。
_String_iterator类的继承关系如下图:
这个类实现了迭代器的通用操作,比如:
- reference operator*() const;
- pointer operator->() const
- _String_iterator & operator++()
- _String_iterator operator++(int)
- _String_iterator& operator--()
- _String_iterator operator--(int)
- _String_iterator& operator+=(difference_type _Off)
- _String_iterator operator+(difference_type _Off) const
- _String_iterator& operator-=(difference_type _Off)
- _String_iterator operator-(difference_type _Off) const
- difference_type operator-(const _Mybase& _Right) const
- reference operator[](difference_type _Off) const
有了迭代器的实现,就可以很方便的使用算法库里面的函数了,比如将所有字符转换为小写:
- string s("Hello String");
- transform(s.begin(), s.end(), s.begin(), tolower);
char_traits类图如下:
这个类定义了字符的赋值,拷贝,比较等操作,如果有特殊需求也可以重新定义这个类。
allocator类图如下:
这个类使用new和delete完成内存的分配与释放等操作。你也可以定义自己的allocator,msdn上有介绍哪些方法是必须定义的。
再看看basic_string类的数据成员:
_Mysize表示实际的元素个数,初始值为0;
_Myres表示当前可以存储的最大元素个数(超过这个大小就要重新分配内存),初始值是_BUF_SIZE-1;
_BUF_SIZE是一个enum类型:
- enum
- { // length of internal buffer, [1, 16]
- _BUF_SIZE = 16 / sizeof (_Elem) < 1 ? 1: 16 / sizeof(_Elem)
- };
从这个定义可以得出,针对char和wchar_t它的值分别是16和8。
_Bxty是一个union:
- union _Bxty
- { // storage for small buffer or pointer to larger one
- _Elem _Buf[_BUF_SIZE];
- _Elem *_Ptr;
- } _Bx;
为什么要那样定义_Bxty呢,看下面这段代码:
- _Elem * _Myptr()
- { // determine current pointer to buffer for mutable string
- return (_BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf);
- }
这个函数返回basic_string内部的元素指针(c_str函数就是调用这个函数)。
所以当元素个数小于_BUF_SIZE时不用分配内存,直接使用_Buf数组,_Myptr返回_Buf。否则就要分配内存了,_Myptr返回_Ptr。
不过内存分配策略又是怎样的呢?看下面这段代码:
- void _Copy(size_type _Newsize, size_type _Oldlen)
- { // copy _Oldlen elements to newly allocated buffer
- size_type _Newres = _Newsize | _ALLOC_MASK;
- if (max_size() < _Newres)
- _Newres = _Newsize; // undo roundup if too big
- else if (_Newres / 3 < _Myres / 2 && _Myres <= max_size() - _Myres / 2)
- _Newres = _Myres + _Myres / 2; // grow exponentially if possible
- //other code
- }
_ALLOC_MASK的值是_BUF_SIZE-1。这段代码看起来有点复杂,简单描述就是:最开始_Myres每次增加_BUF_SIZE,当值达到一定大小时每次增加一半。
针对char和wchar_t,每次分配内存的临界值分别是(超过这些值就要重新分配):
char:15,31,47,70,105,157,235,352,528,792,1188,1782。。。
wchar_t:7, 15, 23, 34, 51, 76, 114, 171, 256, 384, 576, 864, 1296, 1944。。。
重新分配后都会先将旧的元素拷贝到新的内存地址。所以当处理一个长度会不断增长而又大概知道最大大小时可以先调用reserve函数预分配内存以提高效率。
string类占多少字节的内存空间呢?
_Container_base Debug下含有一个指针,4字节,Release下是空类,0字节。_String_val类含有一个allocator对象。string类使用默认的allocator类,这个类没有数据成员,不过按字节对齐的原则,它占4字节。basic_string类的成员加起来是24,所以总共是32字节(Debug)或28字节(Relase)。wstring也是32或28,至于原因文中已经分析。
综上所述:string和wstring类借助_String_iterator实现迭代器操作,都占32(Debug)或28(Release)字节的内存空间,没有虚函数,构造和析构开销较低,内存分配比较灵活。
实际使用string类时也有很多不方便的地方,笔者写了一个扩展类,欢迎提出宝贵意见。
扩展类链接:http://blog.csdn.net/passion_wu128/article/details/38354541
http://blog.csdn.net/passion_wu128/article/details/38353959
C++ string实现原理的更多相关文章
- String类原理分析及部分方法
//String类原理分析及部分方法 //http://www.cnblogs.com/vamei/archive/2013/04/08/3000914.html //http://www.cnblo ...
- java string 细节原理分析(2016.5)
看到了以前2016.5月学习java写的笔记,这里放在一起. String实现的细节原理分析 一.jdk源码中String 的实现 public final class String implemen ...
- Java中String创建原理深入分析
创建String对象的常用方式: 1. 使用new关键字 String s1 = new String(“ab”); // 2. 使用字符串常量直接赋值 String s2 = “abc”; 3 ...
- 5道面试题,拿捏String底层原理!
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. String字符串是我们日常工作中常用的一个类,在面试中也是高频考点,这里Hydra精心总结了一波常见但也有点烧脑的String面试题,一共5道 ...
- java中的String设计原理
首先,必须强调一点:String Pool不是在堆区,也不是在栈区,而是存在于方法区(Method Area) 解析: String Pool是常量池(Constant Pool)中的一块. 我们知 ...
- Delphi中String类型原理介绍
Delphi中字符串的操作很简单,但幕后情况却相当复杂.Pascal传统的字符串操作方法与Windows不同,Windows吸取了C语言的字符串操作方法.32位Delphi中增加了长字符串类型,该类型 ...
- Python string interning原理
原文链接:The internals of Python string interning 由于本人能力有限,如有翻译出错的,望指明. 这篇文章是讲Python string interning是如何 ...
- android开发 WriteUTF与readUTF 原理
今晚上写代码玩,用到java.io.RandomAccessFile.writeUTF(String)函数,而文件默认保存为gbk,显然是乱码.突然想起来去看看存储编码规则,就去找了些文章了解writ ...
- 深入理解String的关键点和方法
String是Java开发中最最常见的,本篇博客针对String的原理和常用的方法,以及String的在开发中常见问题做一个整体性的概括整理.因为之前对String的特性做过一些分析,所以不在详细描述 ...
随机推荐
- git教程--git版本库的使用
向版本控制器提交文件 我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容: Git is a distribute ...
- jquery ajax后台向前台传list 前台用jquery $.each遍历list
$.ajax({ type: 'post', url: xxx.action', dataType: 'text', success: function(data){ var dataObj=eval ...
- 移动端h5摇一摇事件
// 摇一摇动作 //获取加速度信息 //通过监听上一步获取到的x, y, z 值在一定时间范围内的变化率,进行设备是否有进行晃动的判断. //而为了防止正常移动的误判,需要给该变化率设置一个合适的临 ...
- 使用device.js检测设备并实现不同设备展示不同网页
现在很多时候会用@media来控制页面在不同分辨率的设备商展示不同效果,但是有些时候想在直接在PC上展示一个做好的页面,在mobile展示另一个页面.这个时候可以借助device.js来检测设备,然后 ...
- 通过实例深入理解lec和yacc
本框架是一个lex/yacc完整的示例,包括详细的注释,用于学习lex/yacc程序基本的搭建方法,在linux/cygwin下敲入make就可以编译和执行.大部分框架已经搭好了,你只要稍加扩展就可以 ...
- Android Studio安装使用图文教程
原文 http://jingyan.baidu.com/article/1876c852a25cb4890b1376fa.html Google I/O开发者大会上宣布,Android Studio ...
- 给ecshop后台增加管理功能页面
给ecshop后台增加管理功能页面 比如我们增加一个统计报表叫做 物流费用统计报表 放在后台“报表统计”栏目中 具体操作步骤: 第一步,我们要添加一个菜单到后台,然后设置语言项,最后设置权限,这样,后 ...
- 《小C QQ空间转帖、分享工具》之QQ空间数据传递的g_tk算法(C#)
原文地址:http://user.qzone.qq.com/419067339/2 public string GET_HTTP(string url, string referer_post, st ...
- java多线程(同步与死锁问题,生产者与消费者问题)
首先我们来看同步与死锁问题: 所谓死锁,就是A拥有banana.B拥有apple. A对B说:你把apple给我.我就把banana给你. B对A说:你把banana给我,我就把apple给你. 可是 ...
- 华为OJ机试训练(一)
题目1 -- 通过输入英文句子.将每一个单词反过来,标点符号顺序不变.非26个字母且非标点符号的情况就可以标识单词结束. 标点符号包含,.!? 比如输入:Hello, I need an apple. ...