STL:序列式容器vector总结

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

时间: 2024-07-31 11:43:50

STL:序列式容器vector总结的相关文章

C++ STL序列式容器优缺点记录

STL中有三种序列式容器vector,list,deque.对其分别进行性能介绍 vector: vector内部是通过连续的动态内存空间(dynamic array)去管理的,每push_back一个元素,将安插到array尾部. 这种通过连续的内存空间去管理的方式,可以方便我们去通过下标索引到值,在其尾部的插入和删除效率都非常之高. 当在vector内部去进行插入和删除则会导致vector容器元素去移动位置,因此效能较差 deque: deque是一个双端队列,可在首尾进行元素的插入和删除.

STL浅析——序列式容器vector的构造和内存管理: constructor() 和 push_back()

咱们先来做一个测试capacity是容器容量,size是大小: #include <iostream> #include <vector> using namespace std; int main(){ vector<int> result; for (int i = 0; i < 17; i++) { result.push_back(i); printf("element count: %d\t", result.size()); pri

序列式容器————vector

目录 介绍 1 创建 2 容量和大小 size() capacity() 3 resize() 4 reverse() 5 获取元素 front() back() 6 迭代器(待补充) 7 push_back() 8 emplace_back() 9 emplace()(待补充) 10 insert() 11 clear() 12 remove() 13 pop_back() 14 shrink_to_fit() 15 erase() 介绍 和 array<T,N> 容器相似,不同的是 vec

序列式容器----vector

版权声明:本文为博主原创文章,未经博主允许不得转载.

stl源码剖析-序列式容器 之 vector

vector的数据安排以及操作方式,与array(c++自身提供的序列式容器)非常相似.两者唯一的差别在于空间的运用的灵活性.array是静态空间,一旦配置了将不能随意更改其大小,若要更改需要重新配置一块新的空间,如何将元素从旧址中一一搬迁,再释放原来的系统.而vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间已容纳新元素.(实际vector每次动态分配的空间大小是有限的,超过了这个临界值同样是要像array一样进行元素搬迁,下面将会进行详细介绍)vector 是最常用的 C++

STL学习笔记(序列式容器)

Vector Vector是一个动态数组. 1.Vector的操作函数 构造.拷贝和析构 vector<Elem> c //产生一个空vector ,其中没有任何元素 vector<Elem> c1(c2) //产生另一个同型vector的副本,(所有元素都被拷贝) vector<Elem> c(n) //利用元素的default构造函数生成一个大小为n的vector vector<Elem> c(n,elem) //产生一个大小为n的vector,每个元素

《STL源码剖析》学习笔记-第4章 序列式容器

1.vector 1.vector特性 (1)vector有自动扩容操作,每次扩容伴随着"配置新空间 / 移动旧数据 / 释放旧空间"的操作,因此有一定时间成本. (2)vector提供了reserve接口,如果能够对元素个数有大概了解,可以一开始就分配合适的空间. (3)vector的内存空间是连续的,对插入元素的操作而言,在vector尾部插入才是合适的选择.维护的是一个连续线性空间,所以vector支持随机存取. (4)vector动态增加大小时,并不是在原空间之后持续新空间(无

STL标准库-容器-vector

摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. 我对向量容器vector的理解就是一个动态数组,内存连续,它是动态分配内存,且每次扩张的原来的二倍. 他的结构如下 一 定义 vector< 类型 > 标识符(最大容量,初始所有值) vector是一种类模板,那么他有很多行为与类相似 头文件 #include <vector> //a.定义 vector<typeName> v; vector<int> v; //b.拷贝构造

序列式容器

目录 容器结构分类 通过萃取机traits萃取迭代器的型别 容器list list的定义 list的node定义 list的iterator 容器vector vector的定义 vector的iterator vector 的大小 array array的定义 forward_list forward_list与list的区别 deque deque的定义 deque的iterator 容器结构分类 这里的衍生,并非继承关系,而是一种包含关系. 例如heap中包含一个vector. 通过萃取机