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


首先介绍下容器的概念,容器是将一些运用最广的数据结构实现出来,并且根据数据在容器中的排列特性,这些数据结构分为序列式容器以及关联式容器两种。其中序列式容器包括array(c++自建)、vector、heap、priority_queue(由heap演变)、list、slist、deque、stack和queue(由deque演变),关联式容器包括RB-tree、set和map和multiset和multimap(由RB-tree演变)、hashtable、hash_set和hash—map和hash-multiset和hash-multimap(由hashtable演变)。其中序列式容器中的元素都是可序的,但是未必有序,其中stack和queue只是将deque的接口修改了,技术上被归类为一种配接器(adapter)。
vector
vector各个方面和数组array都十分类似,惟一的区别是空间的运用的灵活性,array是一个固定了大小的静态空间,一旦配置之后就不能修改了,然后vector是一个动态的空间,随着新元素的加入,它的内部会自行扩充,vector的扩充也是一个配置新空间、数据移动、释放旧空间的大工程。
vector定义的源码如下所示:

    

    template<class T,class Alloc=alloc>
    class vector
    {
        public:
            typedef T value_type;
            typedef value_type* potinter;
            typedef value_type* iterator;//普通指针
            typedef value_type& reference;
            typedef size_t size_type;
            typedef ptrdiff_t difference_type;
        protected:
            typedef simple_alloc<value_type,Alloc>data_allocator;
            iterator start;
            iterator finish;
            iterator end_of_storage;
            void insert_aux(iterator position ,const T& x);//在position位置前插入x。
            void deallocate(){
                if(start)
                   data_allocator::deallocate(start,end_of_storage-start);这是一个释放内存函数,deallocate,相对于allocator的申请空间
            }
            void fill_initialize(size_type n,const T& value)
            {
                start=allocate_and_fill(n,value);
                finish=start+n;
                end_of_storage=finish;
                //这是一个填充函数,将我们申请的vector的长度为n的空间全部用value进行赋值。
            }
            public:
            iteration begin(){return start;}
            iteration 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);}//返回从begin开始后的第n个元素的引用,这确保了可以赋值成功。
            vector():start(0),finish(0),end_of_storage(0){}
            vector(size_type n,const T& vlaue){fill_initialize(n,value);}
            vector(int n,const T& vlaue){fill_initialize(n,value);}
            vector(long n,const T& vlaue){fill_initialize(n,value);}
            explicit vector(size_type n){fill_initialize(n,T());}//声明为explicit的构造函数不能在隐式转换中使用,T()相当于一个调用默认初始化的构造函数,它的初始化的缺省值为0
            ~vector(){
            destroy(start,finish);//这是个全局函数,是一个析构函数,负责调用类型的析构函数,销毁相应内存上的内容(但销毁后内存地址仍保留)
            deallocate();//负责释放内存(此时相应内存中的值在此之前应调用destory销毁,将内存地址返回给系统,代表这部分地址使用引用-1)
            }
            reference front(){return *begin();}
            reference back(){return *(end()-1);}
            void push_back(const T& x)
            {
                if(finish!=end_of_storage)
                {
                    construct(finish,x);//全局函数,用于将指定指针位置的内容置为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;//如果加一等于end,说明指向的最后一个元素,和pop_back是一样的,如果没有指向最后一个,那么就挨个向前复制。
                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);//如果过短,那么在最后再插入缺少的个数个x;
            }
       protected:
            iterator allocate_and_fill(size_type n,const T& x)
            {
                iterator result=data_allocator::allocate(n);//配置n个元素空间
                uninitialized_fill_n(result,n,x);//全局函数,将范围内指向的所有的未初始化的内存空间赋值x。
                return result; 
            }
    }



因为vector是一个连续的线性空间,所以它的迭代器只要普通指针就可以满足了,并且普通指针也支持随机存取,所以vector提供的是一个random access iterators。它的数据机构很简单,以两个迭代器start以及finish分别指向配置得来的连续空间中目前被使用的范围,以end_of_storage代表整块连续空间的尾端。为了降低空间配置的速度成本,一般情况下实际配置大小会比需求更大一些,这就是capacity的概念,下面这张图可以很好的解释上面这段话:

配置空间的程序为:

    

template<calss T,class Alloc>
    void vector<T,Alloc>::insert_aux(iterator position,const T& x)
    {
        if(finish!=end_of_storage)
       //还有备用空间,在备用空间起始处构造元素,并将最后一个元素值设为其初值。
            construct(finish,*(finish-1));
        ++finish;
       //调整finish的位置。
        T x_copy=x;
      //copy_backward是从后往前赋值,具体见下附图,这样刚好能够不覆盖掉元素。
        copy_backward(position,finsh-2,finish-1);
      //将插入值赋给指针位置position。
        *position =x_copy;
    }
    else//无备用空间
    {
        const size_type old_size=size();//取出原来的大小
        const size_type len=old_size!=0?2*old_size:1;
        //原来大小为0,就配置一个元素空间,否则就是配置原来大小的两倍空间,前半段用来放置原来的数据,后半段放新数据。
        iterator new_start=data_allocator::allocate(len);//配置空间。
        iterator new_finish=new_start;//内部暂无元素
        try{
            new_finish=uninitialized_copy(start,position,new_start);//将start到position位置的元素全部复制到新地址
            construct(new_finish,x);//将待插入的值放入最后可以备用空间
            ++new_finish;//调整位置
            new_finish=uninitialized_copy(position,finish,new_finish);//将之前未复制完的后半段元素复制过来。
        }
        catch(.....){
            destroy(new_start,new_finish);//上述操作失败,那么析构掉已经复制元素的内存空间
            data_allocator::deallocate(new_start,len);//释放掉内存空间
            throw;
        }
        destroy(begin(),end());//析构并释放原来的vector。
        deallocate();
        start=new_start;//调整start和finish和end_of_storage,使它们指向新的vector。
        finish=new_finish;
        end_of_storage=new_start+len;
    }
    }




注意一点,动态增加大小,从上述代码可以看出,并不是在原来的空间之后接上新的空间,而是要经过配置,复制,释放三个操作,因此,对vector的任何操作,一旦引起来空间重新配置,之前的指向原vector的迭代器就失效了,vector里面的函数包括void pop_back()、iterator erase(iterator first,iterator last)、iterator erase(iterator position)、void insert(iterator position ,size_type n,const T&x),挑选其中的几个进行剖析。

    

iterator erase(iterator first,iterator last)
    {
        iterator i=copy(last,finish,first);//全局函数,复制从last到finish的元素到从first开始的内存空间中,从前往后复制后,返回最后一个地址。
        destroy(i,finish);
        finish=finish-(last-first);
        return first;
    }
    iterator erase(iterator position)
    {
        if(position+1!=end())//看是否是清除最后一个元素
            copy(position+1,finish,position)
        --finish;
        destroy(finish);
        return position;
    }
    void clear(){erase(begin(),end());}
    void insert(iterator position, const T& value){
            insert(position, 1, value);
        }
    //在position位置之后,插入n个值为value的元素
    void insert(iterator position, size_type n, const T& value){
        if (n == 0)return;
        if ((end_of_storage - finish) >= n){//备用空间够插入n个新元素
            T x_copy = value;
            const  size_type size_from_position_to_end = finish - position;//计算从最后一个位置到要插入位置的元素个数
            iterator old_finish = finish;
            if (size_from_position_to_end > n)//如果插入元素个数小于插入位置之后的元素个数{
                uninitialized__copy(finish - n, finish, finish);//这是一个用于未初始化空间的复制函数,将倒数的n个元素往未初始化过的内存空间移动
                finish += n;//改变finish的值
                copy_backward(position, old_finish - n, old_finish);从后往前,将剩下的size_from_position_to_end-n个元素从之前的finish那个位置往前复制
                fill(position, position + n, x_copy);//填入新的值
            }
            else{//插入点之后元素个数小于新增元素个数
                uninitialized_fill_n(finish, n - size_from_position_to_end, x_copy);//先用x将finish之后的n-size_from_position_to_end个备用空间赋值。
                finish += n - size_from_position_to_end;更改finish的值
                uninitialized_copy(position, old_finish, finish);一个插入n个值,再将这剩下的size_from_position_to_end个之前的元素赋值到finish之后。
                finish += size_from_position_to_end;修改finish
                fill(position, old_finish, x_copy);用x将已经移动到最后的size_from_position_to_end个元素的空间赋值。
            }
        }
        else{
            //重新申请空间,并且决定新长度为旧长度的两倍或者说旧长度+新增元素个数。
            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;
            //内存的分配要有原子性,即:要么全部成功,要么全部失败。
            try{
                new_finish = uninitialized_copy(begin(), position, new_start);//1.将原内容至position的所有元素(不包含position) 拷贝到新的vector
                new_finish = uninitialized_fill_n(new_finish, n, value);//2.将position位置到后面的n个元素都填充为value
                new_finish = uninitialized_copy(position, end(), new_finish);//3.拷贝从 position位置到end()位置的原vector的所有剩余元素
            }
            catch (...)//如果失败了
            {
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start,len);(等同于free(new_start);)//删除申请到的内存
                
                throw;        //抛出异常
            }
            //析构并释放原vector
            destroy(begin(), end());
            //删除内存
            deallocate();
            //调整迭代器,指向新的vector
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }


上述代码的操作可以用接下来的几幅图进行一个很直观的展示:

 






















 

    










 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值