STL源码剖析 - 第4章 序列式容器 - vector

4.2 vector容器

       在STL编程中,我们最常用到的就是容器,容器可分为序列容器和关联容器;本文记录的是我们经常使用的序列容器之vector,vector的数据安排和操作方式类似于C++内置数组类型array,唯一的区别就是在于空间的灵活运用。内置数组array是静态空间,一旦分配了内存空间就不能改变,而vector容器可以根据用户数据的变化而不断调整内存空间的大小。

       vector容器有已使用空间和可用空间,已使用空间是指vector容器的大小,可用空间是指vector容器可容纳的最大数据空间capacity。vector容器是占用一段连续线性空间,所以vector容器的迭代器就等价于原生态的指针;vector的实现依赖于内存的配置和内存的初始化,以及迭代器。其中内存的配置是最重要的,因为每当配置内存空间时,可能会发生数据移动,回收旧的内存空间,如果不断地重复这些操作会降低操作效率,所有vector容器在分配内存时,并不是用户数据占多少就分配多少,它会分配一些内存空间留着备用,即是用户可用空间。关于vector类定义可参考vector库文件》或者MSDN库的vector类》;以下源代码在SGI STL的文件<stl_vector.h>中。

4.2.3 vector的迭代器

   vector容器维护的空间的线性连续的,所以普通指针也可以作为迭代器,满足vector的访问操作;如:operator*,operator->,operator++,operator--,operator+,operator-,operator+=,operator-=等操作;同时vector容器支持随机访问,所以,vector提供的是随机访问迭代器。

 
  1. //Alloc是SGI STL的空间配置器,默认是第二级配置器

  2. template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >

  3. class vector : protected _Vector_base<_Tp, _Alloc>

  4. {

  5. public://vector的内嵌型别定义,是iterator_traits<I>服务的类型

  6. typedef _Tp value_type;

  7. typedef value_type* pointer;

  8. typedef const value_type* const_pointer;

  9. typedef value_type* iterator;//vector容器的迭代器是普通指针

  10. typedef const value_type* const_iterator;

  11. ...

  12. public://以下定义vector迭代器

  13. iterator begin() { return _M_start; }//指向已使用空间头的迭代器

  14. const_iterator begin() const { return _M_start; }

  15. iterator end() { return _M_finish; }//指向已使用空间尾的迭代器

  16. const_iterator end() const { return _M_finish; }

  17. reverse_iterator rbegin()

  18. { return reverse_iterator(end()); }

  19. const_reverse_iterator rbegin() const

  20. { return const_reverse_iterator(end()); }

  21. reverse_iterator rend()

  22. { return reverse_iterator(begin()); }

  23. const_reverse_iterator rend() const

  24. { return const_reverse_iterator(begin()); }

  25. ...

  26. };


4.2.4 vector容器的数据结构

       vector容器采用的是线性连续空间的数据结构,使用两个迭代器来管理这片连续内存空间,这两个迭代器分别是指向目前使用空间的头start和指向目前使用空间的尾finish,两个迭代器的范围[start,finish)表示容器的大小size()。由于为了提高容器的访问效率,为用户分配内存空间时,会分配多余的备用空间,即容器的容量,以迭代器end_of_storage作为可用空间的尾,则容器的容量capacity()为[start,end_of_storage)范围的线性连续空间。

 
  1. //Alloc是SGI STL的空间配置器,默认是第二级配置器

  2. template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >

  3. class vector : protected _Vector_base<_Tp, _Alloc>

  4. {

  5. ...

  6. protected:

  7. _Tp* _M_start;//表示目前使用空间的头

  8. _Tp* _M_finish;//表示目前使用空间的尾

  9. _Tp* _M_end_of_storage;//表示目前可用空间的尾

  10. ...

  11. };



4.2.5 vector的构造函数和析构函数

       这里把vector容器的构造函数列出来讲解,主要是我们平常使用vector容器时,首先要要定义相应的容器对象,所以,如果我们对vector容器的构造函数了解比较透彻时,在应用当中就会比较得心应手。

 
  1. /*以下是vector容器的构造函数*******************************************

  2. /**********************************************************************

  3. *** //默认构造函数*****************************************************

  4. * explicit vector( const Allocator& alloc = Allocator() ); *

  5. *** //具有初始值和容器大小的构造函数***********************************

  6. * explicit vector( size_type count, *

  7. * const T& value = T(), *

  8. * const Allocator& alloc = Allocator()); *

  9. * vector( size_type count, *

  10. * const T& value, *

  11. * const Allocator& alloc = Allocator()); *

  12. *** //只有容器大小的构造函数*******************************************

  13. * explicit vector( size_type count ); *

  14. *** //用两个迭代器区间表示容器大小的构造函数***************************

  15. * template< class InputIt > *

  16. * vector( InputIt first, InputIt last, *

  17. * const Allocator& alloc = Allocator() ); *

  18. *** //拷贝构造函数*****************************************************

  19. * vector( const vector& other ); *

  20. * vector( const vector& other, const Allocator& alloc ); *

  21. *** //移动构造函数*****************************************************

  22. * vector( vector&& other ); *

  23. * vector( vector&& other, const Allocator& alloc ); *

  24. *** //用初始列表的值构造容器,列表内的元素值可以不同*******************

  25. * vector( std::initializer_list<T> init, *

  26. * const Allocator& alloc = Allocator() ); *

  27. ***********************************************************************/

  28. explicit vector(const allocator_type& __a = allocator_type())

  29. : _Base(__a) {}//默认构造函数

  30. vector(size_type __n, const _Tp& __value,

  31. const allocator_type& __a = allocator_type())

  32. : _Base(__n, __a)//构造函数,里面包含n个初始值为value的元素

  33. //全局函数,填充值函数,即从地址M_start开始连续填充n个初始值为value的元素

  34. { _M_finish = uninitialized_fill_n(_M_start, __n, __value); }

  35. explicit vector(size_type __n)//该构造函数不接受初始值,只接受容易包含元素的个数n

  36. : _Base(__n, allocator_type())

  37. { _M_finish = uninitialized_fill_n(_M_start, __n, _Tp()); }

  38. vector(const vector<_Tp, _Alloc>& __x)

  39. : _Base(__x.size(), __x.get_allocator())//拷贝构造函数

  40. { _M_finish = uninitialized_copy(__x.begin(), __x.end(), _M_start); }

  41. #ifdef __STL_MEMBER_TEMPLATES

  42. // Check whether it's an integral type. If so, it's not an iterator.

  43. /*这个是某个区间的构造函数,首先判断输入是否为整数_Integral()

  44. *采用__type_traits技术

  45. */

  46. template <class _InputIterator>

  47. vector(_InputIterator __first, _InputIterator __last,

  48. const allocator_type& __a = allocator_type()) : _Base(__a) {

  49. typedef typename _Is_integer<_InputIterator>::_Integral _Integral;

  50. _M_initialize_aux(__first, __last, _Integral());

  51. }

  52. template <class _Integer>

  53. //若输入为整数,则调用该函数

  54. void _M_initialize_aux(_Integer __n, _Integer __value, __true_type) {

  55. _M_start = _M_allocate(__n);

  56. _M_end_of_storage = _M_start + __n;

  57. _M_finish = uninitialized_fill_n(_M_start, __n, __value);

  58. }

  59. template <class _InputIterator>

  60. //若输入不是整数,则采用Traits技术继续判断迭代器的类型

  61. void _M_initialize_aux(_InputIterator __first, _InputIterator __last,

  62. __false_type) {

  63. _M_range_initialize(__first, __last, __ITERATOR_CATEGORY(__first));

  64. }

  65. #else

  66. vector(const _Tp* __first, const _Tp* __last,

  67. const allocator_type& __a = allocator_type())

  68. : _Base(__last - __first, __a)

  69. { _M_finish = uninitialized_copy(__first, __last, _M_start); }

  70. #endif /* __STL_MEMBER_TEMPLATES */

  71. ~vector() { destroy(_M_start, _M_finish); }//析构函数

4.2.6 vector的元素操作

   vector容器的成员函数使我们访问容器时经常会用到,为了加深对其了解,这里单独对成员函数源码进行了详细的注解。

    

 
  1. /*以下是容器的一些成员函数*/

  2. size_type size() const//vector容器大小(已使用空间大小),即容器内存储元素的个数

  3. { return size_type(end() - begin()); }

  4. size_type max_size() const//返回可容纳最大元素数

  5. { return size_type(-1) / sizeof(_Tp); }

  6. size_type capacity() const//vector容器可用空间的大小

  7. { return size_type(_M_end_of_storage - begin()); }

  8. bool empty() const//判断容器是否为空

  9. { return begin() == end(); }

  10. reference operator[](size_type __n) { return *(begin() + __n); }//返回指定位置的元素

  11. const_reference operator[](size_type __n) const { return *(begin() + __n); }

  12. #ifdef __STL_THROW_RANGE_ERRORS

  13. //若用户要求的空间大于可用空间,抛出错信息,即越界检查

  14. void _M_range_check(size_type __n) const {

  15. if (__n >= this->size())

  16. __stl_throw_range_error("vector");

  17. }

  18. reference at(size_type __n)//访问指定元素,并且进行越界检查

  19. { _M_range_check(__n); return (*this)[__n]; }//访问前,先进行越界检查

  20. const_reference at(size_type __n) const

  21. { _M_range_check(__n); return (*this)[__n]; }

  22. #endif /* __STL_THROW_RANGE_ERRORS */

  23. void reserve(size_type __n) {//改变可用空间内存大小

  24. if (capacity() < __n) {

  25. const size_type __old_size = size();

  26. //重新分配大小为n的内存空间,并把原来数据复制到新分配空间

  27. iterator __tmp = _M_allocate_and_copy(__n, _M_start, _M_finish);

  28. destroy(_M_start, _M_finish);//释放容器元素对象

  29. _M_deallocate(_M_start, _M_end_of_storage - _M_start);//回收原来的内存空间

  30. //调整迭代器所指的地址,因为原来迭代器所指的地址已经失效

  31. _M_start = __tmp;

  32. _M_finish = __tmp + __old_size;

  33. _M_end_of_storage = _M_start + __n;

  34. }

  35. }

  36. reference front() { return *begin(); }//返回第一个元素

  37. const_reference front() const { return *begin(); }

  38. reference back() { return *(end() - 1); }//返回容器最后一个元素

  39. const_reference back() const { return *(end() - 1); }

  40. void push_back(const _Tp& __x) {//在最尾端插入元素

  41. if (_M_finish != _M_end_of_storage) {//若有可用的内存空间

  42. construct(_M_finish, __x);//构造对象

  43. ++_M_finish;

  44. }

  45. else//若没有可用的内存空间,调用以下函数,把x插入到指定位置

  46. _M_insert_aux(end(), __x);

  47. }

  48. void push_back() {

  49. if (_M_finish != _M_end_of_storage) {

  50. construct(_M_finish);

  51. ++_M_finish;

  52. }

  53. else

  54. _M_insert_aux(end());

  55. }

  56. void swap(vector<_Tp, _Alloc>& __x) {

  57. /*交换容器的内容

  58. *这里使用的方法是交换迭代器所指的地址

  59. */

  60. __STD::swap(_M_start, __x._M_start);

  61. __STD::swap(_M_finish, __x._M_finish);

  62. __STD::swap(_M_end_of_storage, __x._M_end_of_storage);

  63. }

  64. iterator insert(iterator __position, const _Tp& __x) {//把x值插入到指定的位置

  65. size_type __n = __position - begin();

  66. if (_M_finish != _M_end_of_storage && __position == end()) {

  67. construct(_M_finish, __x);

  68. ++_M_finish;

  69. }

  70. else

  71. _M_insert_aux(__position, __x);

  72. return begin() + __n;

  73. }

  74. iterator insert(iterator __position) {

  75. size_type __n = __position - begin();

  76. if (_M_finish != _M_end_of_storage && __position == end()) {

  77. construct(_M_finish);

  78. ++_M_finish;

  79. }

  80. else

  81. _M_insert_aux(__position);

  82. return begin() + __n;

  83. }

  84. void insert (iterator __pos, size_type __n, const _Tp& __x)

  85. { //在pos位置连续插入n个初始值为x的元素

  86. _M_fill_insert(__pos, __n, __x); }

  87. void _M_fill_insert (iterator __pos, size_type __n, const _Tp& __x);

  88. void pop_back() {//取出最尾端元素

  89. --_M_finish;

  90. destroy(_M_finish);//析构对象

  91. }

  92. iterator erase(iterator __position) {//擦除指定位置元素

  93. if (__position + 1 != end())

  94. copy(__position + 1, _M_finish, __position);//后续元素前移一位

  95. --_M_finish;

  96. destroy(_M_finish);//析构对象

  97. return __position;

  98. }

  99. iterator erase(iterator __first, iterator __last) {//擦除两个迭代器区间的元素

  100. iterator __i = copy(__last, _M_finish, __first);//把不擦除的元素前移

  101. destroy(__i, _M_finish);//析构对象

  102. _M_finish = _M_finish - (__last - __first);//调整finish的所指的位置

  103. return __first;

  104. }

  105. void resize(size_type __new_size, const _Tp& __x) {//改变容器中可存储的元素个数,并不会分配新的空间

  106. if (__new_size < size()) //若调整后的内存空间比原来的小

  107. erase(begin() + __new_size, end());//擦除多余的元素

  108. else

  109. insert(end(), __new_size - size(), __x);//比原来多余的空间都赋予初值x

  110. }

  111. void resize(size_type __new_size) { resize(__new_size, _Tp()); }

  112. void clear() { erase(begin(), end()); }//清空容器

  113. // assign(), a generalized assignment member function. Two

  114. // versions: one that takes a count, and one that takes a range.

  115. // The range version is a member template, so we dispatch on whether

  116. // or not the type is an integer.

  117. /*该函数有两种类型:

  118. void assign( size_type count, const T& value );

  119. template< class InputIt >

  120. void assign( InputIt first, InputIt last );

  121. */

  122. //把容器内容替换为n个初始值为value

  123. void assign(size_type __n, const _Tp& __val) { _M_fill_assign(__n, __val); }

  124. void _M_fill_assign(size_type __n, const _Tp& __val);

  125. #ifdef __STL_MEMBER_TEMPLATES

  126. template <class _InputIterator>

  127. void assign(_InputIterator __first, _InputIterator __last) {

  128. typedef typename _Is_integer<_InputIterator>::_Integral _Integral;

  129. _M_assign_dispatch(__first, __last, _Integral());

  130. }

  131. template <class _Integer>

  132. void _M_assign_dispatch(_Integer __n, _Integer __val, __true_type)

  133. { _M_fill_assign((size_type) __n, (_Tp) __val); }

  134. template <class _InputIter>

  135. void _M_assign_dispatch(_InputIter __first, _InputIter __last, __false_type)

  136. { _M_assign_aux(__first, __last, __ITERATOR_CATEGORY(__first)); }

  137. template <class _InputIterator>

  138. void _M_assign_aux(_InputIterator __first, _InputIterator __last,

  139. input_iterator_tag);

  140. template <class _ForwardIterator>

  141. void _M_assign_aux(_ForwardIterator __first, _ForwardIterator __last,

  142. forward_iterator_tag);

  143. #endif /* __STL_MEMBER_TEMPLATES */

      根据以上成员函数的注释,这里对其中几个函数进一步详细的讲解:iterator erase(iterator __first, iterator __last),void insert(iterator __pos, size_type __n, const _Tp& __x);

    其中擦除函数是擦除输入迭代器之间的元素,但是没有回收内存空间,只是把内存空间作为备用空间,首先看下该函数的源代码:

 
  1. iterator erase(iterator __first, iterator __last) {//擦除两个迭代器区间的元素

  2. iterator __i = copy(__last, _M_finish, __first);//把不擦除的元素前移

  3. destroy(__i, _M_finish);//析构对象

  4. _M_finish = _M_finish - (__last - __first);//调整finish的所指的位置

  5. return __first;

  6. }


           根据上面函数的定义,我们可以知道,迭代器start和end_of_storage并没有改变,只是调整迭代器finish,并析构待擦除元素对象;下面通过图解进行分析:

    插入元素函数是在指定位置position上连续插入n个初始值为x的元素,根据插入元素个数和可用空间大小的比较,分别进行不同的初始化,详细见源码分析:

 
  1. void insert (iterator __pos, size_type __n, const _Tp& __x)

  2. { //在pos位置连续插入n个初始值为x的元素

  3. _M_fill_insert(__pos, __n, __x); }

  4. template <class _Tp, class _Alloc>

  5. void vector<_Tp, _Alloc>::_M_fill_insert(iterator __position, size_type __n,

  6. const _Tp& __x)

  7. {

  8. if (__n != 0) {//当n不为0,插入才有效

  9. if (size_type(_M_end_of_storage - _M_finish) >= __n) {//若有足够的可用空间,即备用空间不小于新插入元素个数

  10. _Tp __x_copy = __x;

  11. const size_type __elems_after = _M_finish - __position;//计算插入点之后的现有元素个数

  12. iterator __old_finish = _M_finish;

  13. //case1-1:插入点之后的现有元素个数大于新插入元素个数

  14. if (__elems_after > __n) {

  15. uninitialized_copy(_M_finish - __n, _M_finish, _M_finish);//把[finish-n,finish)之间的数据复制[finish,finish+n)

  16. _M_finish += __n;//调整迭代器finish所指的位置

  17. copy_backward(__position, __old_finish - __n, __old_finish);//把[position,old_finish-n)之间的数据复制[old_finish-n,old_finish)

  18. fill(__position, __position + __n, __x_copy);//在指定位置(插入点)填充初始值

  19. }

  20. //case1-2:插入点之后的现有元素个数不大于新插入元素个数

  21. else {

  22. uninitialized_fill_n(_M_finish, __n - __elems_after, __x_copy);//先在可用空间填入n-elems_after个初始值x

  23. _M_finish += __n - __elems_after;//调整迭代器finish

  24. uninitialized_copy(__position, __old_finish, _M_finish);//把[position,old_finish)之间的数据复制到[old_finish,finish)

  25. _M_finish += __elems_after;

  26. fill(__position, __old_finish, __x_copy);

  27. }

  28. }

  29. //case2:若备用空间小于新插入元素个数

  30. else {//若备用空间小于新插入元素个数,则分配新的空间

  31. //并把原始数据复制到新的空间,调整迭代器

  32. const size_type __old_size = size(); //获取原始空间的大小

  33. //新的空间为旧空间的两倍,或为旧空间+新增长元素个数

  34. const size_type __len = __old_size + max(__old_size, __n);

  35. //配置新的空间

  36. iterator __new_start = _M_allocate(__len);

  37. iterator __new_finish = __new_start;

  38. __STL_TRY {//把插入点之前的原始数据复制到新的空间

  39. __new_finish = uninitialized_copy(_M_start, __position, __new_start);

  40. //将新加入数据添加在[new_finish,new_finish+n)

  41. __new_finish = uninitialized_fill_n(__new_finish, __n, __x);

  42. //将插入点之后的原始数据复制到新空间

  43. __new_finish

  44. = uninitialized_copy(__position, _M_finish, __new_finish);

  45. }

  46. //释放原来空间的对象和内存

  47. __STL_UNWIND((destroy(__new_start,__new_finish),

  48. _M_deallocate(__new_start,__len)));

  49. destroy(_M_start, _M_finish);

  50. _M_deallocate(_M_start, _M_end_of_storage - _M_start);

  51. //调整迭代器所指的位置

  52. _M_start = __new_start;

  53. _M_finish = __new_finish;

  54. _M_end_of_storage = __new_start + __len;

  55. }

  56. }

  57. }


 

    下面对不同情况利用图解方式对插入函数进行分析:

        case1-1:对应的源代码解析中的case1-1的情况;

 

         case1-2:对应源码剖析中的case1-2情况:

       case2:针对源码剖析的case2情况:

vector的操作符重载

    关于操作符重载的这里只给出源代码的注释:

 
  1. template <class _Tp, class _Alloc>

  2. inline bool //操作符重载,判断两个容器是否相等,即容器大小和容器内容是否都相等

  3. operator==(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y)

  4. {

  5. return __x.size() == __y.size() &&

  6. equal(__x.begin(), __x.end(), __y.begin());

  7. /*STL中equal函数的实现如下:

  8. * template<class InputIt1, class InputIt2>

  9. * bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)

  10. * {

  11. * for (; first1 != last1; ++first1, ++first2)

  12. * {

  13. * if (!(*first1 == *first2))

  14. * {

  15. * return false;

  16. * }

  17. * }

  18. * return true;

  19. * }

  20. */

  21. }

  22. template <class _Tp, class _Alloc>

  23. inline bool

  24. operator<(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y)

  25. {

  26. /*函数原型:

  27. template<class InputIt1, class InputIt2>

  28. bool lexicographical_compare(InputIt1 first1, InputIt1 last1,

  29. InputIt2 first2, InputIt2 last2)

  30. {

  31. for ( ; (first1 != last1) && (first2 != last2); first1++, first2++ ) {

  32. if (*first1 < *first2) return true;

  33. if (*first2 < *first1) return false;

  34. }

  35. return (first1 == last1) && (first2 != last2);

  36. }

  37. */

  38. return lexicographical_compare(__x.begin(), __x.end(),

  39. __y.begin(), __y.end());

  40. }

  41. #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER

  42. template <class _Tp, class _Alloc>

  43. inline void swap(vector<_Tp, _Alloc>& __x, vector<_Tp, _Alloc>& __y)

  44. {

  45. __x.swap(__y);

  46. }

  47. template <class _Tp, class _Alloc>

  48. inline bool

  49. operator!=(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y) {

  50. return !(__x == __y);

  51. }

  52. template <class _Tp, class _Alloc>

  53. inline bool

  54. operator>(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y) {

  55. return __y < __x;

  56. }

  57. template <class _Tp, class _Alloc>

  58. inline bool

  59. operator<=(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y) {

  60. return !(__y < __x);

  61. }

  62. template <class _Tp, class _Alloc>

  63. inline bool

  64. operator>=(const vector<_Tp, _Alloc>& __x, const vector<_Tp, _Alloc>& __y) {

  65. return !(__x < __y);

  66. }

  67. #endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */

  68. template <class _Tp, class _Alloc>

  69. vector<_Tp,_Alloc>&

  70. vector<_Tp,_Alloc>::operator=(const vector<_Tp, _Alloc>& __x)

  71. {

  72. if (&__x != this) {

  73. const size_type __xlen = __x.size();

  74. if (__xlen > capacity()) {

  75. iterator __tmp = _M_allocate_and_copy(__xlen, __x.begin(), __x.end());

  76. destroy(_M_start, _M_finish);

  77. _M_deallocate(_M_start, _M_end_of_storage - _M_start);

  78. _M_start = __tmp;

  79. _M_end_of_storage = _M_start + __xlen;

  80. }

  81. else if (size() >= __xlen) {

  82. iterator __i = copy(__x.begin(), __x.end(), begin());

  83. destroy(__i, _M_finish);

  84. }

  85. else {

  86. copy(__x.begin(), __x.begin() + size(), _M_start);

  87. uninitialized_copy(__x.begin() + size(), __x.end(), _M_finish);

  88. }

  89. _M_finish = _M_start + __xlen;

  90. }

  91. return *this;

  92. }

STL源码剖析 - 第4章 序列式容器 - vector_copy(x.begin(), x.begin() + size(), start);-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值