deque源码3(deque的构造与内存、ctor、push_back、push_front)

deque源码1(deque概述、deque中的控制器)

deque源码2(deque迭代器、deque的数据结构) 

deque源码3(deque的构造与内存、ctor、push_back、push_front)

deque源码4(deque元素操作:pop_back、pop_front、clear、erase、insert)

 

deque的构造与内存

deque自行定义了两个专属的空间配置器:

protected:
    typedef simple_alloc<value_type,Alloc> data_allocator; //专属空间配置器,每次配置一个元素大小
    typedef simple_alloc<pointer,Alloc> map_allocator; //专属空间自配器,每次配置一个指针大小

    deque(int n,const value_type& value):
        start(),finish(),map(0_,map_size(0){
            fill_initialize(n,value);
    }
fill_initialize()负责产生并安排好deque的结构,并将元素的初值设定妥当:
    template <class T,class Alloc,size_t BufSize>
    void deque<T.Alloc,BufSize>::fill_initialize(size_type n,const value_type& value){
        create_map_and_nodes(n); //把deque的结构都产生并安排好
        map_pointer cur;
        __STL_TRY{
            //为每个节点的缓冲区设定初值
            for(cur=start.node;cur<finish.node;++cur)
                uninitialized_fill(*cur,*cur+buffer_size(),value):
            uninitialized_fill(finish.first,finish.cur,value);
        }
        catch(...){
            ...
        }
    }

 其中create_map_and_nodes()负责产生并安排好deque的结构:

template <class T,class Alloc,size_t BufSize>
    void deque<T,Alloc,BufSize>::create_map_and_nodes(size_type num_elements){
        //需要的节点数=(元素个数/每个缓冲区可容纳的元素个数)+1
        //如果刚好除整数,会多配置一个节点
        size_type num_nodes=num_elements/buffer_size()+1;
        //一个map要管理几个节点,最少8个,最多是"所需节点数加2"
        //前后各预备一个,扩充时可用
        map_size=max(inital_map_size(),num_nodes+2);
        map=map_allocator::allocate(map_size); //配置出一个"具有map_size个节点"的map
        //以下令nstart和nfinish指向map所拥有之全部节点的最中央区段
        //保持在最中央,可使头尾两端的扩充能量一样大,每个节点可对应一个缓冲区
        map_pointer nstart=map+(map_size-num_nodes)/2;
        map_pointer nfinish=nstart+num_nodes-1;

        map_pointer cur;
        __STL_TRY{
            //为map内的每个现用节点配置缓冲区,所有缓冲区加起来就是deque的可用空间(最后一个缓冲区可能留有一些富裕)
            for(cur=nstart;cur<=nfinish;++cur)
                *cur=allocate_node();
        }
        catch(...){
            //若成功全部执行,不成功一个都不执行
            ...
            //为deque内的两个迭代器start和end设定正确内容
            start.set_node(nstart);
            finish.set_node(nfinish);
            start.cur=start.first;
            finish.cur=finish.first+num_element%buffer_size();//整除时,会多配一个节点,cur指向该节点缓冲区的起始处
        }
    }

举一个例子,代码如下,deque状态如下图:

deque<int> mydeque(20,0);
for(int i=0;i<mydeque.size();i++)
   mydeque[i]=i;
for(int i=0;i<3;i++)
   mydeque.push_back(i);

push_back()函数内容如下:

public:
    void push_back(const value& t){
        if(finish.cur!=finish.last-1){
            //最后缓冲区上有至少一个备用空间
            construct(finish.cur,t); //直接在备用空间上构造元素
            ++finish.cur; //调整最后缓冲区的使用状态
        }
        else //最后缓冲区已无元素备用空间或者只有一个元素备用空间
            push_back_aux(t);
    }

接着上面的例子,再在mydeque后面添加一个元素3,由于尾端只存在一个元素的备用空间,所以必须调用push_back_aux,先配置一整块的缓冲区,再添加新的元素,deque状态如下:

push_back_aux()函数内容如下:

//只有最后一个缓冲区只剩一个备用元素空间时才会被调用
    template <class T,class Alloc,size_t BufSize>
    void deque<T,Alloc,BufSize>::push_back_aux(const value_type& t){
        value_type t_copy=t;
        reserve_map_at_back(); //若符合某种条件则必须重换一个map
        *(finish.node+1)=allocate_node(); //配置一个新的节点(缓冲区)
        __STL_TRY{
            construct(finish.cur,t_copy); //针对标的元素设置
            finish.set_node(finish.node+1); //改变finish,令其指向新节点
            finish.cur=finish.first; //设定finish的状态
        }
        __STL_UNWIND(deallocate_node(*(finish.node+1)));
    }

接着上面的例子,在mydeque的前端插入99,deque状态如下:

push_front()函数操作如下:

public:
    void push_front(const value& t){
        if(satrt.cur!=start.first){
            //第一缓冲区尚有备用空间
            construct(start.cur-1,t); //直接在备用空间上构造元素
            --start.cur; //调整第一缓冲区的使用状态
        }
        else //第一缓冲区已无备用空间
            push_front_aux(t);
    }

由上图可知,这里必须调用push_front_aux(),push_front_aux()函数操作如下:

//只有第一个缓冲区只剩一个备用元素空间时才会被调用
template <class T,class Alloc,size_t BufSize>
void deque<T,Alloc,BufSize>::push_back_aux(const value_type& t){
    value_type t_copy=t;
    reserve_map_at_front(); //若符合某种条件则必须重换一个map
    *(start.node-1)=allocate_node(); //配置一个新的节点(缓冲区)
    __STL_TRY{
        start.set_node(start.node-1); //改变start,令其指向新节点
        start.cur=start.last-1; //设定start的状态
        construct(start.cur,t_copy); //针对标的元素设值
    }
    catch(...){
        //若成功全部执行,若失败全部执行
        start.set_node(start.node+1);
        start.cur=satrt.first;
        deallocate_node(*(start.node-1));
        throw;
    }
}

reserve_map_at_back、reserve_map_at_front

reserve_map_at_back()、reserve_map_at_front()这两个函数会在什么时候调用?答案是它们会在map需要重新整治的时候,也就是map的节点备用空间不足的时候。

reserve_map_at_front()函数操作如下:

void reserve_map_at_front(size_type nodes_to_add=1){
    if(nodes_to_add>start.node-map) //如果map前端的节点备用空间不足,则必须重新换一个map
        reallocate_map(nodes_to_add,true); //配置更大的,拷贝原来的,释放原来的
}

reserve_map_at_back()函数操作如下:

void reserve_map_at_back(size_type nodes_to_add=1){
    if(nodes_to_add+1>map_size-(finish.node-map)) //如果map尾端的节点备用空间不足,则必须重新换一个map
        reallocate_map(nodes_to_add,false); 
}

reallocate_map()函数操作如下:

template <class T,class Alloc,size_t BufSize>
void deque<T,Alloc,BufSize>::reallocate_map(size_type nodes_to_add,bool add_at_front){
    size_type old_num_nodes=finish.node-start.node+1;
    size_type new_num_nodes=old_num_nodes+nodes_to_add;
    map_pointer new_nstart;
    if(map_size>2*new_num_nodes){
        new_nstart=map+(map_size-new_num_nodes)/2+(add_at_front?nodes_to_add:0);
        if(new_nstart<start.node)
            copy(start.node,finish.node+1,new_nstart);
        else
            copy_backward(start.node,finish.node+1,new_nstart+old_num_nodes);
    }
    else{
        size_type new_map_size=map_size+max(map_size,node_to_add)+2;
        //配置一块空间,准备给新map使用
        map_pointer new_map=map_allocator::allocte(new_map_size);
        new_nstart=new_map+(new_map_size-new_num_nodes)/2+(add_at_front?nodes_to_add:0);
        //把原map内容拷贝过来
        copy(start.node,finish.node+1,new_nastart);
        //释放原map
        map_allocator::deallocate(map,map_size);
        //设定新map的起始地址与大小
        map=new_map;
        map_size=new_map_size;
    }
    //重新设定迭代器start和finish
    start.set_node(new_nstart);
    finish.set_node(new_nstart+old_num_nodes-1);
}

 

posted @ 2019-01-09 17:14  ybf&yyj  阅读(1125)  评论(0编辑  收藏  举报