一、首先,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源码剖析》——候捷