• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 探究C++中string类的实现原理以及扩展使用

探究C++中string类的实现原理以及扩展使用

作者:passion_wu128 字体:[增加 减小] 来源:互联网 时间:2017-05-28

passion_wu128 通过本文主要向大家介绍了c++ string类,c++中string类,c++string类源代码,c++中string的用法,c++ string等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

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; 
</div>

从这个定义可以看出string和wstring分别是模板类basic_string对char和wchar_t的特化。

再看看basic_string类的继承关系(类方法未列出):

20151217142549352.png (411×442)

最顶层的类是_Container_base,它也是STL容器的基类,Debug下包含一个_Iterator_base*的成员,指向容器的最开始的元素,这样就能遍历容器了,并定义了了两个函数

void _Orphan_all() const;  // orphan all iterators 
void _Swap_all(_Container_base_secure&) const; // swaps all iterators 
</div>

Release下_Container_base只是一个空的类。

_String_base类没有数据成员,只定义了异常处理的三个函数:

static void _Xlen();  // report a length_error 
static void _Xran();  // report an out_of_range error 
static void _Xinvarg(); 
</div>

_String_val包含一个alloctor的对象,这个类也非常简单,除了构造函数没有定义其它函数。
上面三个基类都定义得很简单,而basic_string类的实现非常复杂。不过它的设计和大多数标准库一样,把复杂的功能分成几部分去实现,充分体现了模块的低耦合。
迭代器有关的操作交给_String_iterator类去实现,元素相关的操作交给char_traits类去实现,内存分配交给allocator类去实现。

_String_iterator类的继承关系如下图:

20151217142657254.png (472×431)

这个类实现了迭代器的通用操作,比如:

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 
</div>

有了迭代器的实现,就可以很方便的使用算法库里面的函数了,比如将所有字符转换为小写:

string s("Hello String"); 
transform(s.begin(), s.end(), s.begin(), tolower); 
</div>

char_traits类图如下:

20151217142733106.png (554×330)

这个类定义了字符的赋值,拷贝,比较等操作,如果有特殊需求也可以重新定义这个类。

allocator类图如下:

20151217142752706.png (524×204)

这个类使用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) 
}; 
</div>

从这个定义可以得出,针对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; 
</div>

为什么要那样定义_Bxty呢,看下面这段代码:

_Elem * _Myptr() 
{  // determine current pointer to buffer for mutable string 
  return (_BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf); 
}
</div>

 
这个函数返回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 
} 
</div>

_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。。。

</div>

重新分配后都会先将旧的元素拷贝到新的内存地址。所以当处理一个长度会不断增长而又大概知道最大大小时可以先调用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类
在实际开发过程中,C++string类使用起来有很多不方便的地方,笔者根据根据这些不足简单的扩展了这个类,如增加与数字之间的相互转化和格式化字符串。不足的地方望指正。读者也可以根据自己需求继续扩展。

头文件:exstring.h

/* 
Author: wuqiang 
Email: debugroot@126.com 
Description:exstring is a subclass of basic_string.It is added some userful 
operations,such as toUpper,toLower,toNumber,fromNumber,format,etc.It can also 
convert between basic_string seamlessly,which is very important for compatibility. 
And it is almostly a wrapper of some C++ standard library,so there should be no bugs. 
If you find some,please let me known.It is totally free,you can use it in any way you desire. 
*/ 
#pragma once 
 
#include <string> 
#include <stdarg.h> 
#include <algorithm> 
#include <sstream> 
#include <iomanip> 
 
using namespace std; 
 
#ifndef INLINE 
#define INLINE inline 
#endif //INLINE 
 
static ios_base::fmtflags BaseFlag(int base) 
{ 
  return (base == 16) ? (ios_base::hex) :  
    ( (base == 8) ? (ios_base::oct) : (ios_base::dec) ); 
} 
 
template<class _Elem> struct ex_char_traits 
{ 
}; 
 
template<> struct ex_char_traits<char> 
{ 
  static INLINE int ct_vscprintf(const char* format, va_list argptr )  
  {  
    return _vscprintf(format, argptr); 
  } 
  static INLINE int ct_vstprintf_s(char* buffer, size_t numberOfEle



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++ 类访问控制的条件总结
  • C++实现String类实例代码
  • C++实现日期类(Date类)的方法
  • C++ string 字符串查找匹配实例代码
  • C++中string的模拟实现
  • 关于C++ string和c类型字符数组的对比
  • C++简单集合类的实现方法
  • 从string类的实现看C++类的四大函数(面试常见)
  • 探究C++中string类的实现原理以及扩展使用
  • 深入解析C++编程中类的封装特性

相关文章

  • 2017-05-28exit和atexit的区别详细解析
  • 2017-05-28C/C++获取目录下的文件列表信息
  • 2017-05-28C语言中的sscanf()函数使用详解
  • 2017-05-28C语言顺序表实现代码排错
  • 2017-05-28C++实现在文本中找出某个单词的位置信息
  • 2017-05-28c语言socket多线程编程限制客户端连接数
  • 2017-05-28C++中关于委派(Delegates)的实现示例
  • 2017-05-28输入3个字符串,将它们按照字母由大到小排序(示例代码)
  • 2017-05-28全面了解#pragma once与 #ifndef的区别
  • 2017-05-28关于C++中的友元函数的一些总结

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • MFC实现在文件尾追加数据的方法
    • C语言判断回文数的小例子
    • C语言实现获取内存信息并输出的实例
    • c语言与c++基础知识点(必看)
    • 简要对比C语言中三个用于退出进程的函数
    • C语言高效编程的几招小技巧
    • C++设计模式编程中使用Bridge桥接模式的完全攻略
    • break的使用for循环嵌套示例
    • 浅谈C语言中的强符号、弱符号、强引用和弱引用
    • c语言中用字符串数组显示菜单的解决方法

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有