Table of Contents
0.概述
vector动态数组, 顾名思义 ,空间可以动态变化的数组,自然与数组array非常相似.
array使用时必须一开始就定好使用空间,定好以后就不能变了,
使用途中要想让空间大一点,必须在重新开辟一块更大的内存空间,然后把原来内存空间的数据都搬运过去.
vector就不一样了,在元素加入的过程中会自动扩充空间,这样就更灵活,没有必要害怕因为内存不足就一开始开辟一个特别大的空间
那么vector是怎么实现这一特性的呢?废话不多说 直接上源码
// alloc 是SGI STL的空间配置器
template<class T,class Alloc=alloc>
class vector{
public:
//vector的嵌套型别定义
typedef T value_type;
typedef value_type* pointer;
typedef value_type* iterator;
typedef value_type* reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
//simple_alloc 是SGI STL的空间配置器
typedef simple_alloc<value_type,Alloc> data_allocator;
iterator start;//表示目前使用空间的头
iterator finish;//表示目前使用空间的尾
iterator end_of_storage;//表示目前可用空间的尾
void insert_aux(iterator position,const T& x);
void deallocate(){
if(start)
data_allocator::deallocate(start,end_of_storage-start);
}
void fill_initialize(size_type n,const T& value)
{
start=allocate_and_fill(n,value);
finish=start+n;
end_of_storage=finsih;
}
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);}
vector():start(0),finish(0),end_of_storage(0){}
vector(size_type n,const T& value){fill_initialize(n,value);}
vector(int n,const T& value){fill_initialize(n,value);}
vector(long n,const T& value){fill_initialize(n,value);}
explicit vector(size_type n){fill_initialize(n,T());}
~vector(){
destroy(start,finish);
deallocate();
}
reference front(){return *begin();}//第一个元素
reference back() {return *(end()-1);}//最后一个元素
void push_back(const T& x){//将元素插入至最尾端
if(finish!=end_of_storage){
construct(finish,x);
++finish;
}
else
insert_aux(end(),x);
}
void pop_back(){//将最尾端元素取出
--finish;
destroy(finish);//全局函数
}
iterator erase(iterator position){//清除某位置上的元素
if(position+1 !=end)
{
copy(position+1,finish,position);//后续元素往前移动
}
--finish;
destroy(finish);
return position;
}
void resize(size_type new_size,const T& x)
{
if(new_size<size())
erase(begin()+new_size,end());
else
insert(end(),new_size-size(),x);
}
void resize(size_type new_size){resize(new_size,T());}
void clear() {erase(begin(),end());}
protected:
//配置空间并填满内容
iterator allocate_and_fill(size_type n,const T& x)
{
iterator result=data_allocator::allocate(n);
uninitialized_fill_n(result,n,x);
return result;
}
};
下面将源码分为几个点依次讲解
1.迭代器(成员变量)
vector维护的是一个连续的线性空间,
由于是连续线性空间,
所以其迭代器所要进行的一些操作比如:*,->,+,-,++,--等等普通的指针都可以满足
所以vector的迭代器就是普通指针.
通过普通指针也可让vector随机存取(所以vector的迭代器是Random Access Iterator)
看一下vector源码中,它的迭代器是怎么定义的
emplate<class T,class Alloc=alloc>
class vector{
public:
typedef T value_type;
typedef value_type* iterator;//vector的迭代器是普通指针
};
因此,如果客户端写出这样的代码:
vector<int>::iterator ivite;
vector<Shape>::iterator svite;
ivite的型别就是int*,svitede 的型别就是Shape*
2.数据结构(成员变量)
在将vector的数据结构之前,先说一下vector的存储方式,
vector在使用时正常来说会有一部分正在使用的空间和一部分备用空间,总的空间大小就叫做容量
容量永远大于等于其大使用空间大小,如果相等就是满载了
增加新元素时,如果元素超过了原来的容量,则下次再有新元素时整个vector就得另觅居所了
在另外一个居所上,容量将扩充至原来的两倍,如果还不够,则继续扩充
这个过程会经历"重新开辟新内存->搬运元素->释放原来的内存"
vector实现的关键在于它在内部定义了三个迭代器(指针),
一个start指向正在使用空间的头,一个fininsh指向正在使用空间的尾,一个end_of_storage表示目前可用空间的尾,
源码如下
template<class T,class Alloc=alloc>
class vector{
...
protected:
iterator start;
iterator finish;
iterator end_of_storage;
};
演示如下图
通过这三个迭代器,就可以实现我们想要的很多操作,比如提供首尾标示,大小,容量,空容器判断,[ ]运算符,最前端元素,最后端元素等
template <class T,class Alloc=alloc>
class vector{
...
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);}
...
};
3.内存管理(成员函数)
先用例子说明一下vector的内存扩容机制
下面将结合它的构造函数说明它是怎么开辟内存的
结合它的主要成员函数(push_back,pop_back,insert.erase,clear)是怎么操作内存的
结合它的析构函数说明它是怎么释放内存的
-
内存扩容
扩容原理概述
新增元素:Vector通过一个连续的数组存放元素,
如果集合已满,在新增数据的时候,就要分配一块更大的内存,
将原来的数据复制过来,释放之前的内存,在插入新增的元素;
对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了 ;
初始时刻vector的capacity为0,塞入第一个元素后capacity增加为1;
不同的编译器实现的扩容方式不一样,VS2015中以1.5倍扩容,GCC以2倍扩容。