标签:
说起数组我们都不陌生,但在C++中,除了数组还多了一个“新朋友”那就是vector。其实vector本质上与array的数据安排以及操作方式也其为相似。它俩唯一的差别就是空间灵活性。
无论在C语言还是C++中,array的空间一旦申请完成就不能进行更改,如果需要更大空间来存储数据,便得重新申请一个新的数组并将原来的数值拷贝过去,然后再将原来数组释放,而这一切都需要用户自己完成。而vector不同的是,它的空间分配更加灵活,当他内存不够存放时,它内部机制会自行进行容量扩充,这对于程序员来说使用起来更加灵活和方便。
vector结构图:
下面我们一起来看看STL中是如何示现vector的。
template <class T, class Alloc = alloc> //定义为模版类,配置空间默认使用alloc。
class vector
{
public: //将类型重命名使用起来更加方便、易懂
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef value_type* iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
//填充并初始化
void fill_initialize(size_type n, const T &value)
{
start = allocate_and_fill(n,value); //构造并填充
finish = start+n; //调整finish指向最后一个元素的下一个空间
end_of_storage = finish; //让end_of_storage指向目前可以使用空间的尾部
}
//构造空间并且填满初始值
iterator allocate_and_fill(size_type n, const T &x)
{
iterator result = data_allocator::allocate(n); //配置n个空间,此时allocate()实质上是使用的时空间配置器中的simple_alloc()函数
uninitialized_fill_n(result,n,x); //用x将n个空间填满
return result; //返回vector的首地址
}
public:
//用初始值构造一个vector
vector():start(0),finish(0),end_of_storage(0){}
//构造一个可存放n个元素的vector,初始值用默认值填充
vector(size_type n)
{
fill_initialize(n,T());
}
~vector()
{}
private:
typedef simple_alloc<value_type, Alloc> data_allocator; //将simple_alloc<value_type, Alloc>重命名
iterator start; //指向目前使用空间的头
iterator finish; //指向目前使用空间的尾
iterator end_of_storage; //指向目前可以使用空间的尾
}
下面是一些简单的接口函数:
iterator begin(){return start;} //得到vector的头部
const_iterator begin() const {return start;} //得到vector头部常方法
iterator end(){return finish;} //得到vector尾部
const_iterator end() const {return finish;} //得到vector尾部的常方法
size_type size(){return size_type(end() - begin());} //获得vector的有效长度
size_type capacity(){return size_type(end_of_storage - begin());} //获得vector的总容量
size_type max_size() const{return size_type(-1) / sizeof(T);} //
bool empty(){return size == 0;} //判空
reference front(){return *begin();} //得到头部的值
const_reference front() const {return *begin;} //得到头部值的常方法
reference back(){return *end();} //得到尾部的值
const_reference back() const {return *(end() - 1);} //得到尾部值的常方法
reference operator[](size_type n){return *(begin() + n);} //重载[],让它和数组一样可以通过下标访问元素的值
const_reference operator[](size_type n)const{return *(begin() + n);} //重载[]的常方法
删除某个位置元素:
iterator erase(iterator position)
{
//把范围在[position+1,finish)内的元素拷贝到以position为起始位置的空间内
if(position + 1 != end()){
copy(position + 1, finish, position);
}
//改变目前finish指向
-- finish;
//销毁finish位置的元素(多于出来的)
destroy(finish);
return position;
}
vector的局部删除函数erase()
//删除first到last之间的元素
iterator earse(iterator first, iterator last)
{
//将last到finish之间的元素拷贝到以first处开始位置,并将finish存放位置返回
iterator i = copy(last, finish, first);
//销毁i到finish位置元素
destroy(i, finish);
//调整目前finish的指向
finish = finish - (last - first);
return first;
}
erase()中copy()主要实现的功能为:
iterator copy(iterator last, iterator finish, iterator first)
{
for(Distance n = last- first; n > 0; --n, ++first, ++last){
*first = *last;
}
return first
扩容:
vector相对于array来说,它的空间看起来可以示现“自我成长”,但其实vector它的“成长”只是一种假象。它还是要进行寻求更大的空间。vector扩容三部曲:(1)、申请足够的、更大的空间。(2)、拷贝原数据到新空间中。(3)、释放原空间。
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());}
插入函数和删除函数:
vector再插入和删除时一般都是尾插和尾删,在源码中vector也只提供了尾插和尾删。因为这是考虑到效率问题,头删和头插效率都很低,因为要牵扯到把所有元素前移的问题,这样它的时间复杂度就与元素个数有关。所以考虑到效率问题,源码中并没有提供前插和前删的接口函数。
尾删:
void pop_back()
{
--finish; //调整finish指向
destroy(finish); //销毁finish
}
尾插:
void push_back(const T &x)
{
if(finish != end_of_storage){ //如果还有备用空间
construct(finish, x); //在finish处用x构造一个节点
++finish; //更改目前finish的指向
}else{
insert_aux(end(), x); //没有备用空间则调动insert_aux()
}
}
Insert_aux()函数的实现:
Insert_aux()中在扩展备用空间时,并不是在原空间后面连接新空间,而是重新配置一个大小为原大小二倍的较大空间。因为我们并无法保证原空间后面还有可供配置的空间。但之所以选择重新配置大小为原大小二倍是因为,为了省去不停扩展空间的麻烦。
void insert_aux(iterator position, const T &x)
{
if(finish != end_of_storage){ //还有备用空间,
construct(finish, *(finish - 1)); //在备用空间起始处构造一个元素。并用vector最后一个元素作为初始值。
++finish; //更改目前finish的指向
T x_copy = x;
copy_backward(position, finish - 2, finish - 1); //下面图解
*position = x_copy; //然后将x_copy值放入要插入的位置
}else{ //没有备用空间
const size_type old_size = size(); //记录原来size
const size_type len = old_size != 0 ? 2 * old_size : 1; //如果原大小为0,则配置1个元素大小;如果原大小不为0,则配置原大小二倍空间。
iterator new_start = data_allocator::allocate(len); //配置大小为len的空间
iterator new_finish = new_start;
new_finish = uninitalized_copy(start, position, new_start); //将[start, position)范围内的元素拷贝到以new_start为起始处
construct(new_finish, x); //用X在position处构造一个元素
++new_finish; //调整new_finish的指向
new_finish = uninitalized_copy(position, finish, new_finish); //再将[position, finish)范围内的元素拷贝到以new_finish为起始的位置。并将新的new_finish指向返回
//析构并时释放原vector
destroy(begin(), end());
deallocate();
//调整迭代器,指向新的vector
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
有备用空间时——copy_back()实现方法:
无备用空间时:
insert()函数:在指定位置处插n个元素,初值为x:
void insert(iterator position, size_type n, const T& x)
{
if(n != 0){ //当n!=0在执行以下操作,否则直接返回
if(size_type(end_of_storage - finish) >= n){ //备用空间 >= 新增元素个数。
T x_copy = x;
const size_type elems_after = finish - position; //得到插入点后元素个数
iterator old_finish = finish;
if(elems_after > n){ //如果插入点后元素个数大于新增元素个数
uninitialized_copy(finish - n, finish, finish); //将[finish-n, finish)范围内的元素移至以finish为起始的地方,这样在finish前空出了n个空间
finish += n; //调整finish目前指向
copy_backward(position, old_finish, finish); //将[position,old_finish)范围内的元素向后移动了(finish-old_finish)【即n】个元素,
finish += elems_after;
fill(position, old_finish, x_copy); //在position出插入新增值
}else{ //插入点元素个数小于新增值个数
uninitialized_fill_n(finish, n - elems_after, x_copy); //在finish出填充入(elems_after - n)个元素,初值为x.使插入点后元素个数等于新增值个数
finish += n - elems_after; //调整finish的指向正确
uninitialized_copy(position, old_finish, finish); //将[position,old_finish)范围内的元素向后移动了(finish-old_finish)【即n】个元素,
finish += elems_after; //调整finish的指向
fill(position, old_finish, x_copy); //在position出插入新增值
}
}else { //备用空间小于新增元素
//重新配置新的vector大小
const size_type old_size = size();
const size_type len = old_size + max(old_size, n);
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
new_finish = uninitialized_copy(start, position, new_start); //将原vector的插入点值前的元素复制到新空间
new_finish = uninitialized_fill_n(new_finish, n, x); //将新增元素填入新的vector(填入以目前new_finish指向的地方开始)
new_finish = uninitialized_copy(position, finish, new_finish); //将原vextor中插入点以后的元素复制到新空间
}
}
}
在指定插入n个元素程序实现过程:
以上就是源码中vector的操作接口。这里代码可能比较零散,而且只有简单的接口程序,如果需要源码可以在下面网址下载:
sgi_STL源码下载地址:http://www.sgi.com/tech/stl/download.html
标签:
原文地址:http://blog.csdn.net/dandelion_gong/article/details/51366460