• 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);
    }
  • 相关阅读:
    FastCgi与PHP-fpm之间是个什么样的关系
    MySql的like语句中的通配符:百分号、下划线和escape
    mysql将int 时间类型格式化
    navicat 结合快捷键
    MySQL中concat函数
    case when then else end 用法
    PhpStorm主题
    Having与Where的区别
    无需图形界面环境下的浏览器
    Socket常见错误
  • 原文地址:https://www.cnblogs.com/ybf-yyj/p/10236104.html
Copyright © 2020-2023  润新知