[STL]源码解析:std::vector

本文探讨了std::vector的内存管理策略,当空间用完时,vector会重新分配更大的内存并复制原有元素。介绍了_Vector_base类的作用以及vector类的模板参数和成员。还讲解了vector的容量、大小、空状态、元素删除等操作的实现细节,并警告了内存重新分配可能导致迭代器失效的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、首先,vector没有那么牛逼,需要一个元素就”只“分配一个元素的空间。从c/c++语言的角度,我想不到有什么办法可以在不改变原有内存array的基础上,动态的增加内存(除非使用链表,这代价过高,无必要)。vector的策略是当已分配的内存n用完时,就重新分配块更大的内存n+m(并非是n+1,而m的具体值不同的STL实现不定),然后将原来的元素复制过来,并释放掉之前的内存。

知道了vector的大致原理,再来看其如何实现。

二、_Vector_base

1. 官方说:此类提供了一个目的,其构造函数和析构函数分配内存(但并不初始化)。使异常安全更容易。这个类是个模板类

template <class _Tp, class _Alloc>

_Tp是其元素类型,_Alloc是其内存分配器(allocator)。

2. 别名

  typedef _Alloc allocator_type;
  typedef _Tp* pointer;
  typedef _STLP_alloc_proxy<pointer, _Tp, allocator_type> _AllocProxy;

3. 成员变量

  pointer _M_start;
  pointer _M_finish;
  _AllocProxy _M_end_of_storage;
_M_start和_M_finish指向连续空间中已使用的范围。

《STL源码解析》中讲,_M_end_of_storage也是个pointer,指向已分配的连续空间的末尾。但是,STLport将其处理为_AllocProxy,使用了一个代理来管理。_AllocProxy有个成员_M_data,实际上指向了已分配的连续空间的末尾。至于AllocProxy的其他功能,不要在意这些细节--!。

三、vector类的定义

实际上vector是有两个模板参数的模板类,但一般情况下只指定第一个,而第二个为默认的allocator

template <class _Tp, _STLP_DFL_TMPL_PARAM(_Alloc, allocator<_Tp>) >    // STLP..宏实际就是_Alloc=allocator<_Tp>
class vector : protected _STLP_PRIV _Vector_base<_Tp, _Alloc>
从基类看出,给_Vector_base传的第二个模板也是_Alloc,即allocator<_Tp>

四、定义别名

typedef _STLP_PRIV _Vector_base<_Tp, _Alloc> _Base;
typedef vector<_Tp, _Alloc> _Self;
上面两个是private。

  typedef typename _Base::allocator_type allocator_type;
  typedef _Tp value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
  typedef random_access_iterator_tag _Iterator_category;
还有两个别名是通过宏_STLP_DECLARE_RANDOM_ACCESS_REVERSE_ITERATORS定义的。

#define _STLP_DECLARE_RANDOM_ACCESS_REVERSE_ITERATORS \
        _STLP_DECLARE_REVERSE_ITERATORS(reverse_iterator)
#    define _STLP_DECLARE_REVERSE_ITERATORS(__reverse_iterator) \
   typedef _STLP_STD::reverse_iterator<const_iterator> const_reverse_iterator; \
   typedef _STLP_STD::reverse_iterator<iterator> reverse_iterator

五、常用的一些public方法

1. begin

iterator begin()             { return this->_M_start; }
const_iterator begin() const { return this->_M_start; }
这里注意到,iterator是value_type*而并非真的迭代器,难道STLport偷工减料用指针代替迭代器,再起个名字“冒充”?——开玩笑,因为指针天生就有vector的迭代器所需的操作,比如opeartor*, operator->, operator++等。

2. end

  iterator end()               { return this->_M_finish; }
  const_iterator end() const   { return this->_M_finish; }

3. rbegin

  reverse_iterator rbegin()              { return reverse_iterator(end()); }
  const_reverse_iterator rbegin() const  { return const_reverse_iterator(end()); }

4. size

size_type size() const        { return size_type(this->_M_finish - this->_M_start); }
5. max_size

  size_type max_size() const {
    size_type __vector_max_size = size_type(-1) / sizeof(_Tp);
    typename allocator_type::size_type __alloc_max_size = this->_M_end_of_storage.max_size();
    return (__alloc_max_size < __vector_max_size)?__alloc_max_size:__vector_max_size;
  }

由于size_type即size_t为unsigned int。故而size_type(-1)=4294967295(即为unsigned int所能表示的最大值)。vector允许的元素个数最多为此。

_M_end_of_storage.max_size()是从allocator继承而来的方法。

从代码逻辑来看,max_size从两方面得到max_size,一是由allocator分配的内存的最大值,二是“固有最大值”。

所以max_size并非是vector已从的元素数(从代码角度理解)。

在STLport实现中有如下结果:

	std:vector<int> a;
	vector<float> b;
	vector<double> d;
	cout << a.max_size() << endl << b.max_size() << endl << d.max_size() << endl;
上述代码的输出是:

1073741823

1072741823

536870911

可见返回的是系统允许的最大容量。

6. capacity

max_size返回最大容量,capacity返回当前容量,注意区别。

size_type capacity() const    { return size_type(this->_M_end_of_storage._M_data - this->_M_start); }
注意与size的区别。

7. empty

bool empty() const            { return this->_M_start == this->_M_finish; }
8. push_back

void push_back(const _Tp& __x) {
   if (this->_M_finish != this->_M_end_of_storage._M_data) {					// 如果到达现有容量的尾部
      _Copy_Construct(this->_M_finish, __x);							// *this->_M_finish = __x
      ++this->_M_finish;
    }
    else {
      typedef typename __type_traits<_Tp>::has_trivial_assignment_operator _TrivialCopy;
      _M_insert_overflow(this->_M_finish, __x, _TrivialCopy(), 1, true);
    }
  }

函数关键在于,如果已分配的内存“不足”,要重新分配内存,这是一个相当复杂的过程,包括重新配置,元素移动,释放空间等等操作。

由于vector的迭代器是指针,因而一旦重新分配了内存,vector的所有迭代器都会失效,盲目使用就会出错。

9. pop_back

删除最后一个元素

  void pop_back() {
    --this->_M_finish;
    _STLP_STD::_Destroy(this->_M_finish);
  }
单纯的_M_finish减1后删除元素。而_Destroy最终调用到

inline void __destroy_aux(_Tp* __pointer, const __false_type& /*_Trivial_destructor*/)
{ __pointer->~_Tp(); }
调用了__pointer的析构函数。为什么是手动调用其析构函数而不使用delete呢?况且,如果是c的内置类型,可以这样玩吗?

从代码中可以看到,vector并没有判断是否为空就盲目的删除最后一个元素,如果一个vector本身为空,执行pop_back就会出错。

10. erase

earse有两个版本:

iterator erase(iterator __pos);
iterator erase(iterator __first, iterator __last);

先行知识:

代理模式

适配器模式

参考:

[1] 《STL源码剖析》——候捷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值