STL源码之vector
STL源码之vector
1. SGI的vector
SGI stl vector继承子一个基类:
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
在基类中定义了基本的一些操作,并且封装了了vector所需要的基本的三个指针:
struct _Vector_impl
: public _Tp_alloc_type
{
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
…
}
public:
_Vector_impl _M_impl;
以_M_impl作为数据成员。
现在我们只在一个类中表达出相同含义。
2. vector基本含义
#include <memory> template <typename T, typename Alloc = std::allocator<T> > class vector { public: //必须的嵌套类型,必须包含iterator_traits中五个类型 typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef Alloc allocator_type; protect: iterator start; //目前使用空间的头:beginn() iterator finish; //目前使用空间尾:end() iterator end_of_storage; //目前可用空间的尾:capacity() //分配内存时,实际分配的类型。 //对于vector,值类型是T,实际分配的类型也是T,other 就是 Alloc<T> //但是对于其它一些类型就不一定了。 //比如:list的值类型为T,实际分配的类型是其list_node<T>类型。 typedef typename template rebind<T>::other T_alloc_type; T_alloc_type& get_T_allocator() noexcept() { return *start; } const T_alloc_type& get_T_allocator() const noexcept() { return *start; } allocator_type get_allocator() const noexcept() { return allocator_type(get_T_allocator()); } 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); } //主要的构造函数和复制控制函数 vector():start(0), finish(0), end_of_storage(0) {} vector(size_type n, const T& value) { fill_initialize(n, value); } explicit vector(size_type n) { default_initialize(n); } vector(const vector& x) { create_storage(n); this->finish = std::uninitialized_copy(x.begin(), x.end(), this->start); } b private: create_storage(size_t n) { this->start = Alloc.allocate(n); this->finish = this->start; this->end_of_storage = this->start + n; } void fill_initialize(size_type n, const value_type& value) { create_storage(n); std::uninitialized_fill_n(start, n, value); this->finish = this->storage; } #if __cplusplus >= 201103L // Called by the vector(n) constructor. void default_initialize(size_type n) { create_storage(n); std::uninitialized_default_n(start, n); this->finish = end_of_storage; } #endif }
3.构造函数基本的要点
(1) vector<T> vec; //默认构造函数使start = finish = end_of_storage = 0;
(2) vector<T> vec(10); //构造10个T()的元素,finish = end_of_storage = start + 10;
capacity()与size()相同。
(3) vector<T> vec(10, value); //构造10个值为value的元素,finish = end_of_storage = start + 10;
capacity()与size()相同。
(4) vector<T> vec(vec2); //vec的容量与大小相同
4. 插入的特点
(1)insert(position, value);
if (finish != end_of_storage) { //剩余空间足够:直接构造元素,调整finish。
//在finish构造元素,值为最后一个元素值
construct(finish, *(finish -1 ));
++finish;
T x_copy = x;
//把postion到最后一个元素之前的元素向后插入到最后位置
copy_backward(position, finsish - 2, finish - 1);
*posion = x_copy;
} else { //没有剩余空间
//size()扩大两倍(VS增加大小的一半).原本为0,则size() = 1; capacity = 1;
//将vector中start..position中元素拷贝到新空间
//在position构造元素
//在把之后的元素复制到新空间中
//释放原本空间
destroy(begin(), end());
deallocate();
}
(2) insert(position, n, value);
if (n != 0) {
if (备用空间 >= n) {
if (positionz之后元素 > n) {
先把尾部前的n个元素移到后面
把position后剩余元素向后复制到尾部前
填充n个新值
} else {
现在尾部fill元素少的元素
调整finish
把元素复制到新尾部
fill剩余的元素
}
} else {
//size()扩大两倍或者原本长度 + n
//将vector中start..position中元素拷贝到新空间
//在position构造n个元素
//在把之后的元素复制到新空间中
//释放原本空间
destroy(begin(), end());
deallocate();
}
}
总结:一旦剩余空间不够,就需要扩充空间,复制元素,释放原空间。所以可以事先确定好大小或者容量。
5. 关于大小和容量
(1) resize(n)时,n >= size()使用默认构造初始化多的元素。n < size()截断元素。
(2) vector<T> vec;
vec.reserve(n); //start = finish = 0; end_of_storage = start + n;
容器中没有元素,此时可以vec[i],但是finish不会改变,即size()还是==0;
此时要想正确的改变size(),必须使用push_back()或者insert()。
整体行为就是,若n > 现有容量,扩大容量。把旧元素复制到新的空间,然后释放原空间。否则,忽略。
(3) erase(position); erase(first, last);时:将后面元素复制到erase的位置(就是直接覆盖),
只是改变了size()。元素capacity()没有改变。
clear()是同样的情景,clear() = earse(begin(), end());
(4) 一般情况下容量从不改变,改变的只是size()。
shrink_to_fit()可以压缩容量的函数。
~vector(); //可以释放空间,size() = capacity() = 0;