常用的数据结构包括array、list、tree、stack、queue、map等等,array和list是其中最基本的。
vector的数据安排和操作方式和array是很相似的,唯一的区别在于vector提供了更加灵活的操作性。array是静态空间,vector可以进行动态的扩充,其本质上来说也是依赖于array,对用户而言,进行了封装,vector内部已经处理了array空间不足的种种状况。vector的实现技术,关键在于对大小的控制以及重新配置时数据移动效率,在此前提下面,很多东西就比较好理解了。
SGI对vector的定义如下,由于篇幅问题,有些类函数单独抽出来了。
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class vector : protected _Vector_base<_Tp, _Alloc>
{
// requirements:
__STL_CLASS_REQUIRES(_Tp, _Assignable);
private:
typedef _Vector_base<_Tp, _Alloc> _Base;
public:
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 typename _Base::allocator_type allocator_type;
allocator_type get_allocator() const { return _Base::get_allocator(); }
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
typedef reverse_iterator<const_iterator> const_reverse_iterator;
typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
typedef reverse_iterator<const_iterator, value_type, const_reference,
difference_type> const_reverse_iterator;
typedef reverse_iterato<iterator, value_type, reference, difference_type>r
reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
protected:
#ifdef __STL_HAS_NAMESPACES
using _Base::_M_allocate;
using _Base::_M_deallocate;
using _Base::_M_start;
using _Base::_M_finish;
using _Base::_M_end_of_storage;
#endif /* __STL_HAS_NAMESPACES */
protected:
void _M_insert_aux(iterator __position, const _Tp& __x);
void _M_insert_aux(iterator __position);
public:
iterator begin() { return _M_start; }
const_iterator begin() const { return _M_start; }
iterator end() { return _M_finish; }
const_iterator end() const { return _M_finish; }
reverse_iterator rbegin()
{ return reverse_iterator(end()); }
const_reverse_iterator rbegin() const
{ return const_reverse_iterator(end()); }
reverse_iterator rend()
{ return reverse_iterator(begin()); }
const_reverse_iterator rend() const
{ return const_reverse_iterator(begin()); }
size_type size() const
{ return size_type(end() - begin()); }
size_type max_size() const
{ return size_type(-1) / sizeof(_Tp); }
size_type capacity() const
{ return size_type(_M_end_of_storage - begin()); }
bool empty() const
{ return begin() == end(); }
}
从上面可以看到,就这一部分而言,vector的实现还是比较简单的,利用指针作为iterator,主要是因为vector是连续的内存空间,指针就可以实现iterator要求的功能。vector继承了_Vector_base,_Vector_base的定义如下:
template <class _Tp, class _Alloc>
class _Vector_base {
public:
typedef _Alloc allocator_type;
allocator_type get_allocator() const { return allocator_type(); }
_Vector_base(const _Alloc&)
: _M_start(0), _M_finish(0), _M_end_of_storage(0) {}
_Vector_base(size_t __n, const _Alloc&)
: _M_start(0), _M_finish(0), _M_end_of_storage(0)
{
_M_start = _M_allocate(__n);//分配内存空间,使用简单的allocate
_M_finish = _M_start;
_M_end_of_storage = _M_start + __n;
}
~_Vector_base() { _M_deallocate(_M_start, _M_end_of_storage - _M_start); }//销毁内存空间,使用的是simple_alloc::deallocate函数实现的。
protected:
_Tp* _M_start;
_Tp* _M_finish;
_Tp* _M_end_of_storage;
typedef simple_alloc<_Tp, _Alloc> _M_data_allocator;
_Tp* _M_allocate(size_t __n)
{ return _M_data_allocator::allocate(__n); }
void _M_deallocate(_Tp* __p, size_t __n)
{ _M_data_allocator::deallocate(__p, __n); }
};
vector_base里面用了三个指针,_M_start, _M_finish, _M_end_of_storage来界定vector的存储空间,vector使用连续的线性空间,就不难理解可以用指针来界定它的大小了。
_M_allocate用来分配内存空间,这两个函数的原型如下:
typedef simple_alloc<_Tp, _Alloc> _M_data_allocator;
_Tp* _M_allocate(size_t __n)
{ return _M_data_allocator::allocate(__n); }
void _M_deallocate(_Tp* __p, size_t __n)
{ _M_data_allocator::deallocate(__p, __n); }
可以看到,vector_base 或者说vector在进行内存分配的时候,采用的是simple_alloc,而不是STL自带的alloc,这个主要是vector需要的是连续的内存空间,采用simple_alloc更好。
上面vector的几个函数里面,都是比较直观的。vector进行数据初始化、删除数据和插入数据的时候,会引起内存的变化,这个时候就需要有一定的机制来调整内存大小了、
vector的push_back原型如下:
void push_back(const _Tp& __x) {
if (_M_finish != _M_end_of_storage) {//判断是否已经到了申请的可用内存的边界
construct(_M_finish, __x);//直接构造函数
++_M_finish;
}
else
_M_insert_aux(end(), __x);//已经到了内存的边界了,需要调用
}
_M_insert_aux函数主要是负责在指定位置插入变量x,其原型为:
void
vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
{
if (_M_finish != _M_end_of_storage) {//如果还有分配的空间,直接构造一下就可以了
construct(_M_finish, *(_M_finish - 1));
++_M_finish;
_Tp __x_copy = __x;
copy_backward(__position, _M_finish - 2, _M_finish - 1);
*__position = __x_copy;
}
else {
const size_type __old_size = size();
const size_type __len = __old_size != 0 ? 2 * __old_size : 1;//如果之前的空间大小为0,那么申请1个空间,否的话就身亲原来2倍的空间,这样就
iterator __new_start = _M_allocate(__len);//可以避免频繁的申请内存空间和移动数据,毕竟这种开销还是挺大的。
iterator __new_finish = __new_start;
__STL_TRY {
__new_finish = uninitialized_copy(_M_start, __position, __new_start);//将position之前的数据复制到新位置
construct(__new_finish, __x);//填充新数据
++__new_finish;//更新起点,将后面的数据都拷贝过来
__new_finish = uninitialized_copy(__position, _M_finish, __new_finish);
}
__STL_UNWIND((destroy(__new_start,__new_finish),
_M_deallocate(__new_start,__len)));
destroy(begin(), end());//调用析构函数
_M_deallocate(_M_start, _M_end_of_storage - _M_start);//释放内存空间
_M_start = __new_start;
_M_finish = __new_finish;
_M_end_of_storage = __new_start + __len;//更新内存起点地址
}
}
按照这个申请空间的方式,我们可以知道vector 的内存大小事1,2,4,8,16,成2的指数倍增长的。从这点来看,如果vector很大的话,那么浪费的内存还是蛮多的。而且速度也会受到比较大的影响,之前做oj的时候,发现用vector跑出来的时间就会偏大。
在处理完内存之后,调用destroy进行析构,destroy函数的原型如下:
template <class _ForwardIterator>
inline void destroy(_ForwardIterator __first, _ForwardIterator __last) {
_Destroy(__first, __last);
}
destroy会直接调用_Destroy函数
template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
__destroy(__first, __last, __VALUE_TYPE(__first));
}
_Destroy调用函数__destroy,进行类型推导。在这个推导过程中,宏定义__VALUE_TYPE是用来判断是否是基本类的。
#define __VALUE_TYPE(__i) value_type(__i)
这个就是用来返回value_type*的指针,用于传给__destroy函数进行推导
template <class _Iter>
inline typename iterator_traits<_Iter>::value_type*
__value_type(const _Iter&)
{
return static_cast<typename iterator_traits<_Iter>::value_type*>(0);
}
这个destroy函数就是最终的分支函数,在这里进行了T的类型推导,交给void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type)或者__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)函数处理。
template <class _ForwardIterator, class _Tp>
inline void
__destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*)
{
typedef typename __type_traits<_Tp>::has_trivial_destructor
_Trivial_destructor;
__destroy_aux(__first, __last, _Trivial_destructor());
}
这里,__destroy的第三个参数是指针,这样就清晰了,可以使用traits技术,实现类型推导。 这里用指针的好处是可以方便处理const p*、p*、p等。
通过type traits技术,最终会调用到这里进行分支
template <class _ForwardIterator>
void
__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
{
for ( ; __first != __last; ++__first)
destroy(&*__first);
}
template <class _ForwardIterator>
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}
可以看出来,对于基本数据类型,是不会进行任何处理的。
这样就最终实现了vector内存的动态加载,insert和push_back都是通过调用_M_insert_aux来实现数据增加的。至于pop_back等操作,就是比较简单的对指针进行--就可以了。
vector的iterato就是指针,所以在进行iterator移动处理的时候,用的是random_access的标签。
参考:
《STL源码剖析》