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.
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 :
真值: −(00000001)2
原码: (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类其实只要知道内部使用_M_start ,_M_finish,_M_end_of_storage来维护就可以了。
我们挑几个常用的成员函数来分析其源码。
默认构造函数:
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.