STL 源码分析之string(一)基础篇

来源:互联网 发布:apache 开源框架 编辑:程序博客网 时间:2024/06/05 09:46

STL源码下载:

https://www.sgi.com/tech/stl/download.html
vs工程代码:http://download.csdn.net/download/jmh1996/10032316

其中string类需要在3.1以后的版本才有定义。

源码分析:

typedef basic_string<char>    string;

string类是由模板类basic_string< class _CharT,class _traits,class _alloc>实例化生成的一个类。

我们现在来看basic_string模板类。

basic_string是由_String_base继承而来的子类。

我们还需要先看_String_base.

_String_base类

template <class _Tp, class _Alloc> class _String_base {public:  typedef _Alloc allocator_type;  allocator_type get_allocator() const { return allocator_type(); }protected:  typedef simple_alloc<_Tp, _Alloc> _Alloc_type;  _Tp* _M_start;  _Tp* _M_finish;  _Tp* _M_end_of_storage;                                // Precondition: 0 < __n <= max_size().  _Tp* _M_allocate(size_t __n) { return _Alloc_type::allocate(__n); }  void _M_deallocate(_Tp* __p, size_t __n) {    if (__p)      _Alloc_type::deallocate(__p, __n);   }  void _M_allocate_block(size_t __n) {     if (__n <= max_size()) {      _M_start  = _M_allocate(__n);      _M_finish = _M_start;      _M_end_of_storage = _M_start + __n;    }    else      _M_throw_length_error();  }  void _M_deallocate_block()     { _M_deallocate(_M_start, _M_end_of_storage - _M_start); }  size_t max_size() const { return (size_t(-1) / sizeof(_Tp)) - 1; }  _String_base(const allocator_type&)    : _M_start(0), _M_finish(0), _M_end_of_storage(0) { }  _String_base(const allocator_type&, size_t __n)    : _M_start(0), _M_finish(0), _M_end_of_storage(0)    { _M_allocate_block(__n); }  ~_String_base() { _M_deallocate_block(); }  void _M_throw_length_error() const;  void _M_throw_out_of_range() const;};#endif /* __STL_USE_STD_ALLOCATORS */// Helper functions for exception handling.template <class _Tp, class _Alloc> void _String_base<_Tp,_Alloc>::_M_throw_length_error() const {  __STL_THROW(length_error("basic_string"));}template <class _Tp, class _Alloc> void _String_base<_Tp, _Alloc>::_M_throw_out_of_range() const {  __STL_THROW(out_of_range("basic_string"));}

公有函数:

public:  typedef _Alloc allocator_type;  allocator_type get_allocator() const { return allocator_type(); }

allocator_type是分配类型,指示了_String_base里面每个元素的类型。
get_allocator()函数则是调用该类型的默认构造函数,返回一个该类型的对象或变量。

维护变量:

protected:  typedef simple_alloc<_Tp, _Alloc> _Alloc_type;  _Tp* _M_start;  _Tp* _M_finish;  _Tp* _M_end_of_storage;

_Alloc_type其实是一个动态内存分配器。
_M_start指向内存区的开始,_M_finish指向当前已经使用了的内存的末尾,_M_end_of_storage指向动态内存区的末尾。
与vector类似, _M_start <= _M_finish<=_M_end_of_storage .

_M_end_of_storage与_M_finish的含义是不同的。

假设申请了16个字节的内存块,现在已经用了10个字节了,那么_M_end_of_storage指向这个大内存块的末尾,而_M_finish指向的是第11个字节。

max_size()函数

 size_t max_size() const { return (size_t(-1) / sizeof(_Tp)) - 1; }

该函数用于返回本系统允许返回的容许的最大元素个数,元素类型为_TP.
size_t(-1)/sizeof(_Tp)是什么含义呢?
size_t(-1)其实是生成一个值为-1的无符号int.
上面代码等价于:

unsigned int t =-1;rerturn t /sizeof(_Tp)-1;

-1在内存中其实表示为(FFFFFFFF)16
因为-1:
-1 :
真值:000000012
原码:(10000000000000000000000000000001)2
反码:(11111111111111111111111111111110)2
补码:(11111111111111111111111111111111)2
这个数字就是当前环境最大的寻址地址。

_Tp* _M_allocate(size_t __n) { return _Alloc_type::allocate(__n); }

该函数调用_Alloc_type内存分配类的内存分配函数,用于动态申请一个可以容纳__n个_Tp类型的元素的内存块。

  void _M_allocate_block(size_t __n) {     if (__n <= max_size()) {      _M_start  = _M_allocate(__n);      _M_finish = _M_start;      _M_end_of_storage = _M_start + __n;    }    else      _M_throw_length_error();  }

上面函数用于申请一个__n个元素的内存块,由上面可知,
_M_start指向内存块的首地址,_M_end_of_storage指向这个块的末尾。当__n超过机器所支持的最大元素个数时则抛出长度异常。

 void _M_throw_length_error() const; template <class _Tp, class _Alloc> void _String_base<_Tp,_Alloc>::_M_throw_length_error() const {  __STL_THROW(length_error("basic_string"));}

两个构造函数:

默认构造:

_String_base(const allocator_type&)    : _M_start(0), _M_finish(0), _M_end_of_storage(0) { }

分配__n个元素空间的构造函数:

  _String_base(const allocator_type&, size_t __n)    : _M_start(0), _M_finish(0), _M_end_of_storage(0)    { _M_allocate_block(__n); 

_String_base类总结

_String_base类其实只要知道内部使用_M_start ,_M_finish,_M_end_of_storage来维护就可以了。

basic_string类

我们挑几个常用的成员函数来分析其源码。

构造函数:

默认构造函数:

explicit basic_string(const allocator_type& __a = allocator_type())    : _Base(__a, 8) { _M_terminate_string(); }

不带任何参数的默认构造函数会申请一个含8个元素的内存块。
_M_terminate_string();函数会将_M_finish所指的内存赋值为0.

指定字符串长度的构造函数:

  struct _Reserve_t {};  basic_string(_Reserve_t, size_t __n,               const allocator_type& __a = allocator_type())    : _Base(__a, __n + 1) { _M_terminate_string(); }

实际上会申请一个长度为__n+1个元素的内存块。

复制构造函数:

  basic_string(const basic_string& __s) : _Base(__s.get_allocator())     { _M_range_initialize(__s.begin(), __s.end()); }

在类似于:

string s1="1234"string s2(s1);

的代码中
将会调用上面这个复制构造函数。
其过程为:
1.首先调用_String_base的构造函数,将_M_finish,_M_start,_M_end_of_storage全部置为0。
2.调用_M_range_initialize()进行赋值:

我们看_M_range_initialize函数:

  void _M_range_initialize(const _CharT* __f, const _CharT* __l) {    ptrdiff_t __n = __l - __f;    _M_allocate_block(__n + 1);    _M_finish = uninitialized_copy(__f, __l, _M_start);    _M_terminate_string();  }

过程为:
1.计算s.end()-s.begin();其实就是_M_finish-_M_start.因为s.end()返回s._M_finish而s.begin()返回s._M_start.
这个计算的就是s的有效字符串的长度__n。
2.申请一个包含__n+1个元素的内存块。
3.调用uninitialized_copy函数将从s._M_start到s._M_finish的内存拷贝到以_M_start为首地址的内存块中。换句话说,就是将s的内存拷贝到本实例的有效区域中。
4.将_M_finish后面的内存写为0.

接受字符串的构造函数:
类似于在string s(“123”)这种情况下会调用。

  basic_string(const _CharT* __s, size_type __n,               const allocator_type& __a = allocator_type())     : _Base(__a)     { _M_range_initialize(__s, __s + __n); }  basic_string(const _CharT* __s,               const allocator_type& __a = allocator_type())    : _Base(__a)     { _M_range_initialize(__s, __s + _Traits::length(__s)); }

过程是计算字符串的长度,然后把字符串的首地址和末地址传入_M_range_initialize()函数中。

生成多个相同字符的string的构造函数
类似于:string s(10,’a’);等效于:string s(“aaaaaaaaaa”)

  basic_string(size_type __n, _CharT __c,               const allocator_type& __a = allocator_type())    : _Base(__a, __n + 1)  {    _M_finish = uninitialized_fill_n(_M_start, __n, __c);    _M_terminate_string();  }

关键是调用了uninitialized_fill_n()函数,该函数首先申请一个长度为__n个元素的内存块,然后再把每个元素的值赋值为__c.

原创粉丝点击