码迷,mamicode.com
首页 > 其他好文 > 详细

STL:序列式容器vector总结

时间:2016-05-13 15:08:59      阅读:229      评论:0      收藏:0      [点我收藏+]

标签:

说起数组我们都不陌生,但在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

STL:序列式容器vector总结

标签:

原文地址:http://blog.csdn.net/dandelion_gong/article/details/51366460

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!