vector
对于容器vector,他的存储原理就是一个数组,对于数组空间申请的话,那么就是基于https://welsey.blog.csdn.net/article/details/112189216中的空间分配器去进行空间申请;
vector核心成员数据:
pointer _M_start; // 开始存储元素的空间地址
pointer _M_finish; // 最后一个存储元素的之后的一个位置的空间地址
pointer _M_end_of_storage; // 整个存储空间的后一个位置的空间地址
这里有必要提一下这三个指针,另外还有就是整个STL的区间习惯,整个STL一般上涉及到区间的[first, last)都是左闭右开的形式;另外就是STL的右开就是只是获取对应的地址,而不获取对应地址的数据,这是c++允许的;
另外还有vector的迭代器,容器的迭代器的话,一般而言,迭代器的移动与取值都与容器本身的数据结构是息息相关的,因此对于STL的每个容器,都定义了一个属于自己的迭代器,vector的迭代器就是normal_iterator<_Tp*>,这里的normal_iterator迭代器操作上来看还是个指针,只不过添加了一些STL迭代器的一些需要的信息;关于迭代器提取器的话iterator_traits,他会提取迭代器中需要的属性,而将指针转换成normal_iterator<_Tp*>形式的意义也在于此;
typedef typename __traits_type::iterator_category iterator_category;
typedef typename __traits_type::value_type value_type;
typedef typename __traits_type::difference_type difference_type;
typedef typename __traits_type::reference reference;
typedef typename __traits_type::pointer pointer;
那么接下来根据增、删、修改访问这三个角度对其进行解析:
增:
容器增加一个元素,STL一般的接口就是insert接口,该接口的话代码如下
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
insert(iterator __position, const value_type& __x)
{
const size_type __n = __position - begin();
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage && __position == end())
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
++this->_M_impl._M_finish;
}
else
{
#if __cplusplus >= 201103L
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Tp __x_copy = __x;
_M_insert_aux(__position, std::move(__x_copy));
}
else
#endif
_M_insert_aux(__position, __x);
}
return iterator(this->_M_impl._M_start + __n);
}
首先呢,vector对于数据的存储时一直保持从开头到end()连续的状态的,因此插入元素位置会影响到具体操作:
- 对于留有存储空间并且插入位置在end()位置的话,直接在end()位置进行元素添加的话:这种情况直接在end()位置进行输入插入并移动原本容器的end()向后一个单元即可;
- 对于留有存储空间但是插入元素不在end()位置的情况,需要将插入位置[insertPos, last)整体后移一位,然后将元素插入;
- 对于没有存储空间的情况下,就需要另外申请一段更大的空间,然后将原本的数据复制到新的空间中,当然,这里有个技巧就是复制的时候就将待插入位置留出来,然后进行两个分区间复制即可;当然,还要最后的destory原存储空间工作;
删:
删除元素的话一般是调用erase接口,删除一个元素,由于容器存储空间足够,因此不必考虑空间扩展,当然,此处我认为也可以加入适当检测,当元素少于容器容量1/4就进行容器缩减,但是STL中并没有这样的操作,具体代码如下:
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::erase(iterator __position)
{
/* 如果删除的元素位置不在vector尾部,就需要进行元素移动 */
if (__position + 1 != end()) _GLIBCXX_MOVE3(__position + 1, end(), __position);
--this->_M_impl._M_finish;
/* 只是进行空间析构,并没有delete空间 */
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
return __position;
}
很简单,就是直接移动区间,然后进行一次对象析构即可;
修改:
对于容器元素的操作一般通过迭代器去操作,当然,也可以重载后的operator[]去操作,operator[]的话,代码如下:
reference
operator[](size_type __n)
{ return *(this->_M_impl._M_start + __n); }
原理就是直接根据指针移动到需要的index索引位置,返回对应数据的引用,这样就可以对容器对应位置的数据进行读写;
当然,通过迭代器也可以实现这样的目的,说道迭代器,vector的迭代器本质上就是个指针套了一个normal_iterator迭代器模板类的壳子,以下就是该迭代器的源码:
template<typename _Iterator, typename _Container>
class __normal_iterator
{
protected:
_Iterator _M_current;
/** 迭代器属性,在该类型中定义了,需要再进行一次typedef扩展出来,才能在本结构中任意使用这些typedef
iterator_traits对指针进行了偏特化,定义了对应的迭代器属性,而一般的迭代器属性直接基于模板参数_Iterator中的属性进行提取。*/
typedef iterator_traits<_Iterator> __traits_type;
public:
typedef _Iterator iterator_type;
typedef typename __traits_type::iterator_category iterator_category;
typedef typename __traits_type::value_type value_type;
typedef typename __traits_type::difference_type difference_type;
typedef typename __traits_type::reference reference;
typedef typename __traits_type::pointer pointer;
_GLIBCXX_CONSTEXPR __normal_iterator() : _M_current(_Iterator()) { }
explicit
__normal_iterator(const _Iterator& __i) : _M_current(__i) { }
// Allow iterator to const_iterator conversion
template<typename _Iter>
__normal_iterator(const __normal_iterator<_Iter,
typename __enable_if<
(std::__are_same<_Iter, typename _Container::pointer>::__value),
_Container>::__type>& __i)
: _M_current(__i.base()) { }
// Forward iterator requirements
/** normal_iterator的operator*取值返回的左值引用,而move_iterator的operator*取值返回的右值引用,两者的区别在于对重载函数的调用,右值接口一般就是用于数据移动的,会对传入的右值进行数据的直接移动,而不是复制操作. */
reference operator*() const
{
return *_M_current;
}
pointer
operator->() const
{ return _M_current; }
__normal_iterator&
operator++()
{
++_M_current;
return *this;
}
__normal_iterator
operator++(int)
{ return __normal_iterator(_M_current++); }
// Bidirectional iterator requirements
__normal_iterator&
operator--()
{
--_M_current;
return *this;
}
__normal_iterator
operator--(int)
{ return __normal_iterator(_M_current--); }
// Random access iterator requirements
reference
operator[](const difference_type& __n) const
{ return _M_current[__n]; }
__normal_iterator&
operator+=(const difference_type& __n)
{ _M_current += __n; return *this; }
__normal_iterator
operator+(const difference_type& __n) const
{ return __normal_iterator(_M_current + __n); }
__normal_iterator&
operator-=(const difference_type& __n)
{ _M_current -= __n; return *this; }
__normal_iterator
operator-(const difference_type& __n) const
{ return __normal_iterator(_M_current - __n); }
const _Iterator&
base() const
{
return _M_current;
}
};
从以上源码可以解析,该迭代器如果传入模板类型是个指针的话,他做的任意接口操作都可以等同于一个指针,因此对于通过迭代器进行数据存取这种方式,本身就可以将其看作为一个指针操作。

浙公网安备 33010602011771号