顺序容器vector
vector的数据安排以及操作和array相似,二者的唯一的差别在于空间的运用灵活性:array 是静态空间,一旦配置之后就不能改变;vector是动态空间,随着元素的加入,它的内部机制会自行的扩充空间以容纳新的元素。
vector的实现技术,关键在于对其大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满载,vector将会以一种空间配置策略来扩充空间,从而降低配置新空间、数据移动、释放旧空间带来的性能消耗。
vector类的结构图
通过上面的类结构图,可以知道类的成员变量定义在_Vector_impl(这个pointer 就是 value_type* ) 中,以下为其源码:
struct _Vector_impl
: public _Tp_alloc_type
{
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
_Vector_impl()
: _Tp_alloc_type(), _M_start(0), _M_finish(0), _M_end_of_storage(0)
{ }
_Vector_impl(_Tp_alloc_type const& __a) _GLIBCXX_NOEXCEPT
: _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
{ }
#if __cplusplus >= 201103L
_Vector_impl(_Tp_alloc_type&& __a) noexcept
: _Tp_alloc_type(std::move(__a)),
_M_start(0), _M_finish(0), _M_end_of_storage(0)
{ }
#endif
void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
{
std::swap(_M_start, __x._M_start);
std::swap(_M_finish, __x._M_finish);
std::swap(_M_end_of_storage, __x._M_end_of_storage);
}
};
其中定义的成员含义如下:
通过以下源码来理解上述成员:
iterator
begin() _GLIBCXX_NOEXCEPT
{ return iterator(this->_M_impl._M_start); }iterator
end() _GLIBCXX_NOEXCEPT
{ return iterator(this->_M_impl._M_finish); }size_type
size() const _GLIBCXX_NOEXCEPT
{ return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }size_type
capacity() const _GLIBCXX_NOEXCEPT
{
return size_type(this->_M_impl._M_end_of_storage
- this->_M_impl._M_start);
}
vector的内存管理
_Vector_base
专门负责vector
的内存管理,内部类_M_impl
通过继承_Tp_alloc_type
(也就是allocator)得到内存分配释放的功能,_M_allocate和_M_deallocate分别分配和释放vector所用内存,vector只需要负责元素构造和析构。
_Vector_base的源码如下:
template<typename _Tp, typename _Alloc>
struct _Vector_base
{
typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
rebind<_Tp>::other _Tp_alloc_type;
typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer
pointer;
struct _Vector_impl
: public _Tp_alloc_type
{
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
_Vector_impl()
: _Tp_alloc_type(), _M_start(0), _M_finish(0), _M_end_of_storage(0)
{ }
_Vector_impl(_Tp_alloc_type const& __a) _GLIBCXX_NOEXCEPT
: _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
{ }
#if __cplusplus >= 201103L
_Vector_impl(_Tp_alloc_type&& __a) noexcept
: _Tp_alloc_type(std::move(__a)),
_M_start(0), _M_finish(0), _M_end_of_storage(0)
{ }
#endif
void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
{
std::swap(_M_start, __x._M_start);
std::swap(_M_finish, __x._M_finish);
std::swap(_M_end_of_storage, __x._M_end_of_storage);
}
};
public:
typedef _Alloc allocator_type;
_Tp_alloc_type&
_M_get_Tp_allocator() _GLIBCXX_NOEXCEPT
{ return *static_cast<_Tp_alloc_type*>(&this->_M_impl); }
const _Tp_alloc_type&
_M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
{ return *static_cast<const _Tp_alloc_type*>(&this->_M_impl); }
allocator_type
get_allocator() const _GLIBCXX_NOEXCEPT
{ return allocator_type(_M_get_Tp_allocator()); }
_Vector_base()
: _M_impl() { }
_Vector_base(const allocator_type& __a) _GLIBCXX_NOEXCEPT
: _M_impl(__a) { }
_Vector_base(size_t __n)
: _M_impl()
{ _M_create_storage(__n); }
_Vector_base(size_t __n, const allocator_type& __a)
: _M_impl(__a)
{ _M_create_storage(__n); }
#if __cplusplus >= 201103L
_Vector_base(_Tp_alloc_type&& __a) noexcept
: _M_impl(std::move(__a)) { }
_Vector_base(_Vector_base&& __x) noexcept
: _M_impl(std::move(__x._M_get_Tp_allocator()))
{ this->_M_impl._M_swap_data(__x._M_impl); }
_Vector_base(_Vector_base&& __x, const allocator_type& __a)
: _M_impl(__a)
{
if (__x.get_allocator() == __a)
this->_M_impl._M_swap_data(__x._M_impl);
else
{
size_t __n = __x._M_impl._M_finish - __x._M_impl._M_start;
_M_create_storage(__n);
}
}
#endif
~_Vector_base() _GLIBCXX_NOEXCEPT
{ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage
- this->_M_impl._M_start); }
public:
_Vector_impl _M_impl;
pointer
_M_allocate(size_t __n)
{
typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
return __n != 0 ? _Tr::allocate(_M_impl, __n) : 0;
}
void
_M_deallocate(pointer __p, size_t __n)
{
typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
if (__p)
_Tr::deallocate(_M_impl, __p, __n);
}
private:
void
_M_create_storage(size_t __n)
{
this->_M_impl._M_start = this->_M_allocate(__n);
this->_M_impl._M_finish = this->_M_impl._M_start;
this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
}
};
下面将通过push_back的源码来了解vector的内存管理:
void
push_back(const value_type& __x)
{
//如果还有备用空间,则构造元素,并调整finish
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,__x);
++this->_M_impl._M_finish;
}
else
_M_insert_aux(end(), __x);//如果当前备用空间不足,调用_M_insert_aux
}
_M_insert_aux的源码如下:
template<typename _Tp, typename _Alloc>
void
vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
//在有备用空间的情况下执行,在备用空间的起始位置构建一个新元素,以最后的元素为初值
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
_GLIBCXX_MOVE(*(this->_M_impl._M_finish
- 1)));
++this->_M_impl._M_finish;//调整finish的位置
_Tp __x_copy = __x;
//这里用于insert时,插入点后元素的移动处理,在push_back并无元素被操作
_GLIBCXX_MOVE_BACKWARD3(__position.base(),
this->_M_impl._M_finish - 2,
this->_M_impl._M_finish - 1);
* __position = __x_copy;
}
else
{
//获取当前实际配置大小,为当前size的2倍,源码见下方
const size_type __len =
_M_check_len(size_type(1), "vector::_M_insert_aux");//插入点之前的元素数目
const size_type __elems_before = __position - begin();
pointer __new_start(this->_M_allocate(__len));
pointer __new_finish(__new_start);
__try
{
//在插入点构造元素,并赋予初值__x
_Alloc_traits::construct(this->_M_impl,
__new_start + __elems_before,
__x);__new_finish = 0;
//将之前的所有元素move到新的vector当中
__new_finish
= std::__uninitialized_move_if_noexcept_a
(this->_M_impl._M_start, __position.base(),
__new_start, _M_get_Tp_allocator());++__new_finish;//更新finsh的位置
//将插入点的原来的元素也copy到插入点的下一个位置
__new_finish
= std::__uninitialized_move_if_noexcept_a
(__position.base(), this->_M_impl._M_finish,
__new_finish, _M_get_Tp_allocator());
}
__catch(...)
{
if (!__new_finish)
_Alloc_traits::destroy(this->_M_impl,
__new_start + __elems_before);
else
std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
_M_deallocate(__new_start, __len);
__throw_exception_again;
}//析构 并释放原vector
std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
_M_get_Tp_allocator());
_M_deallocate(this->_M_impl._M_start,
this->_M_impl._M_end_of_storage
- this->_M_impl._M_start);//调整迭代器,到新的vector
this->_M_impl._M_start = __new_start;
this->_M_impl._M_finish = __new_finish;
this->_M_impl._M_end_of_storage = __new_start + __len;
}
}
_M_check_len的源码:
size_type
_M_check_len(size_type __n, const char* __s) const
{
//内存的分配是有限制的
if (max_size() - size() < __n)
__throw_length_error(__N(__s));//__len为原size的2倍
const size_type __len = size() + std::max(size(), __n);
return (__len < size() || __len > max_size()) ? max_size() : __len;
}
_M_allocate的源码,调用_Vector_base中的内存分配函数:
pointer
_M_allocate(size_t __n)
{
typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
return __n != 0 ? _Tr::allocate(_M_impl, __n) : 0;
}
通过上面源码的分析,可以表示为如下的内存分配策略:
vector的元素操作
1. pop_back
void
pop_back() _GLIBCXX_NOEXCEPT
{
//前移finish,并析构finish指向的元素(前闭后开区间)
--this->_M_impl._M_finish;
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
}
2. erase(iterator __position)调用了_M_erase,_M_erase的源码如下:
template<typename _Alloc>
typename vector<bool, _Alloc>::iterator
vector<bool, _Alloc>::
_M_erase(iterator __position)
{
//只是将删除位置之后的元素前移,然后更新finish位置,但是不知道为什么没有析构元素???
if (__position + 1 != end())
std::copy(__position + 1, end(), __position);
--this->_M_impl._M_finish;
return __position;
}