定义
相比较于array这种静态的数组来说,vector是一种可以动态增长的数组。vector可以保证数据在逻辑和物理上都是连续的,因此在原来的空间上,假设数组的空间不够,此时需要重新开辟空间,但是重新开辟的空间不一定和原空间是相连的,因此为了保证vector的数据的连续性,vector在扩容的时候是需要有数据的搬运过程的。
class的声明
template<class T, class Alloc = alloc>
class vector{
public:
typedef T value_type;
typedef value_type iterator;
typedef value_type& reference;
typedef size_t size_type;
protected:
iterator start;
iterator finish;
iterator end_of_storage;
public:
iterator begin(){return start;}
iterator end(){return finish;}
size_type size() const{return size_type(end() - begin());}
size_type capacity() const{return size_type(end_of_storage - begin());}
bool empty() const{return begin() == end();}
reference operator[](size_type n){return *(begin() + n);}
reference front(){return *begin();}
reference back(){return *(end() - 1);}
};
vector设计中最主要的就是三个iterator。start,finish,end_of_sotrage分别存的是当前存储数组元素的内存空间的起始地址,最后一个元素的下一个地址,连续空间的地址。
- begin()返回起始地址
- end()返回元素的尾地址
- size()当前存了多少个元素
- capacity当前空间最多可以存多少元素
- empty()当前空间是否为空
- []运算符重载,范围数组元素
- front()取vector中的第一个元素
- end()取vector中的最后一个元素
size和capacity
- capacity是当空间不够时,系统一次allocate的空间的大小。对于下图左边的空间来说,capacity=8
- size是指当前数组存的元素的个数,size=6.
size永远小于或者等于capacity.当size>capacity,空间扩容,同时将现有的数据搬到新的空间中去。
新扩的空间大小一般是按照原有空间的2倍大小增大。
push_back
对于vector常用的push_back来说,这里就会存在扩容的问题,源代码如下。push_back的逻辑很简单,检测当前空间是否够存,够的话,我就调用全局函数construct将元素存到内存中去;不够的话,调用辅助的函数来插入。
template<class T, class Alloc>
void vector<T, Alloc>::push_back(const T& x){
if(finish != end_of_storage){//还有空间
construct(finish, x);//全局函数
++finish;
}else{
insert_aux(end(), x);//调用下面的辅助的函数。
}
}
辅助函数有两个参数position和x,position代表新插入元素的位置,x代表新插入的数值。这个辅助函数除了被push_back调用以外,还会被insert调用(在任意位置插入元素,这种插入也可能会导致扩容)。
void vector<T, Alloc>::insert_aux(iterator position, const T& x){
//这里重新检测就是因为除了push_back调用,还会有insert调用 ,所以要检查
if(finish != end_of_storage){//空间够
construct(finish, *(finish - 1));
++finish;
T x_copy = x;
copy_backward(position, finish - 2, finish - 1);
*position = x_copy;
}else{//空间不够
const size_type old_size = size();
//扩容,扩大为原来的两倍,当原数组为空,给分两个空间
const size_type len = old_size != 0 ? 2 * old_size : 1;
//数据搬运
//首先先分配空间
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
//进行数据搬运
try{
//根据原来的start,position(也就是尾地址),copy到新地址(new_start),返回当前新地址的最后一个元素的下一个地址
new_finish = uninitialized_copy(start, position, new_start);
//将新插入的x放入新的尾地址
construct(new_finish, x);
//指针+1
++new_finish;
//这里有这行是因为这个函数对于insert函数来说,因为可能在中间进行插入,因此涉及到扩容后分两段搬运的问题。
new_finish = uninitialized_copy(position, finish, new_finish);
}
catch(...){
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
//释放原来的vector的空间
destroy(begin(), end());
deallocate();
//调整新的迭代器的指向
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
vector的扩容存在下面几个问题:
- 大量调用拷贝构造函数和析构函数
- 设计数据间的搬运
vector的迭代器的设计
vector的迭代器相较于list专门写了一个类来处理,他可以直接声明指针就好了。
template<class T, class Alloc = alloc>
class vector{
public:
typedef T value_type;
typedef value_type* iterator;//T*
};
找iterator的特性
vector<int> vec;
vector<int>::iterator iter = vec.begin();
//找iterator_category
iterator_traits<iter>::iterator_category;
//找difference——type
iterator_traits<iter>::difference_type
//找指向的value——type
iterator_traits<iter>::value_type;