STL:序列式容器vector总结

来源:互联网 发布:usb电流测试软件 编辑:程序博客网 时间:2024/06/05 16:11

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

0 0
原创粉丝点击