STL源码剖析——deque的实现

STL源码剖析——deque的实现原理和使用方法详解 Deque 简介     deque是“double—ended queue”的缩写,和vector一样都是STL的容器,deque 是双端数组,而 vector 是单端的。     deque 在接口上和 vector 非常相似,在许多操作的地方可以直接替换。     deque 可以随机存取元素(支持索引值直接存取,用[]操作符或at()方法,这个等下会详讲)。     deque 头 阅读详情

deque简介

deque是一个双向开口的容器,可在头尾两端插入和删除元素,deque由动态的连续空间组合而成,因为迭代器的良好设计,提供了随机访问,造成一种deque为连续空间的假象

deque的数据结构

deque有一个二级指针map,map指向一小块空间,其中的每个元素都是指针,指向一段连续线性空间,称为缓冲区,缓冲区为deque存储的主体,其中存放元素

tempalte <class T, class Alloc = alloc>
class deque {
public:
    typedef T   value_type;
    typedef T*  pointer;
    typedef T** map_pointer;
protected:
    iterator  start;
    iterator  finish;
    map_pointer map;
    size_type size;         //map内指针个数
    ......
}

iterator 结构如下:

template <class T>
class deque_iterator {
public:
    typedef T**    map_pointer;
    T*  cur;            //指向所指缓冲区当前元素
    T*  first;          //首元素
    T*  last;           //尾元素后一个
    map_pointer node;   //指向控制中心
}    

用图表示如下
这里写图片描述

deque的主要操作

1.push_front

这里写图片描述

当start指向的缓冲区无备用空间时,执行push_front_aux
push_front_aux操作如下:
判断map前端的备用节点是否充足,如果充足,则再分配一个缓冲区,start.node - 1指向它,然后改节点,构造元素即可
如果map前端备用节点不足,如果map_size够大,则并不需要重新分配map, 只需要移动map中的元素即可,最后重置start, finish迭代器即可
如果map_size不够大,则要重新分配map

2.push_back

操作与push_front类似

3.pop_front

void pop_front() {
    if (start.cur != start.last - 1) {
        //第一缓冲区有多个元素
        destroy(start.cur);
        ++start.cur;
    }
    else
        pop_front_aux();
}

void pop_front_aux() {
    destroy(start.cur);
    deallocate_node(start.first);      //回收缓冲区空间
    start.set_node(start.node + 1);    //重置迭代器
    start.cur = start.first;
}

4.pop_back

void pop_back() {
    if (finish.cur != finish.first) {
        //最后缓冲区有元素
        --finish.cur;
        destroy(finish.cur);
    }
    else
        pop_back_aux();
}

void pop_back_aux() {
    deallocate_node(finish.first);    //回收缓冲区空间
    finish.set_node(finish.node - 1); //重置迭代器
    finish.cur = finish.last - 1;
    destroy(finish.cur);
}

deque迭代器完整实现

template <class T>
class deque_iterator {
public:
    typedef deque_iterator<T>           iterator;
    typedef random_access_iterator_tag  iterator_category;
    typedef T                           value_type;
    typedef T*                          pointer;
    typedef T&                          reference;
    typedef ptrdiff_t                   difference_type;
    typedef T**                         map_pointer;
    typedef deque_iterator              self;

    T*  cur;            //指向所指缓冲区当前元素
    T*  first;          //首元素
    T*  last;           //尾元素后一个
    map_pointer node;   //指向控制中心

    deque_iterator():cur(0), first(0), last(0), node(0){}
    deque_iterator(const deque_iterator& other):
        cur(other.cur), first(other.first), last(other.last), node(other.node){}

  static size_t buffer_size() {
       return deque_buffer_size(sizeof(T));
   }

   //重新设置指向管控中心的指针,修改first, last指向缓冲区
   void set_node(map_pointer new_node) {
       node = new_node;
       first = *new_node;
       last = *new_node + (difference_type)buffer_size();
   }

   reference operator* () const {
       return *cur;
   }

   pointer operator-> () const {
       return &(operator*());
   }

   difference_type operator- (const self& x) const {
       return difference_type((buffer_size() * (node - x.node - 1))
           + (cur - first) + (x.last - x.cur));
   }
   //前置++
   self& operator++ () {
       ++cur;
       if (cur == last) {
           set_node(node + 1);
           cur = first;
       }
       return *this;
   }
   //后置++
   self operator++ (int) {
       self temp = *this;
       ++*this;
       return temp;
   }
   //前置--
   self& operator-- () {
       if (cur == first) {
           set_node(node - 1);
           cur = last;
       }
       --cur;
       return *this;
   }
   //后置--
   self operator-- (int) {
       self temp = *this;
       --*this;
       return temp;
   }

   //实现随机存取
   self& operator+= (difference_type n) {
       difference_type offset = n + cur - first;
       size_t size = buffer_size();
       if (offset >= 0 && offset < size)
           cur += n;
       else {
           //不在同一缓冲区内
           difference_type node_offset = offset > 0 ?
               offset / size :
               -difference_type((-offset - 1) / size) - 1;
           //重置node,设置cur指向
           set_node(node + node_offset);
           cur = first + (offset - node_offset * size);//offset正负都适用
       }
       return *this;
   }

   self operator+ (difference_type n) const {
       self temp = *this;
       return temp += n;
   }

   self& operator-= (difference_type n) {
       return *this += -n;
   }

   self operator- (difference_type n) const {
       self temp = *this;
       return temp -= n;
   }

   //重载[],随机存取
   reference operator[] (difference_type n) const {
       return *(*this + n);
   }

   bool operator== (const self& x) { return cur == x.cur; }
   bool operator!= (const self& x) { return cur != x.cur; }
   bool operator< (const self& x) {
       return (node == x.node) ? (cur < x.cur) : (node < x.node);
   }
};

为了将deque的迭代器使用起来看似是普通指针需要费些功夫

小结:

由deque的构造我们可以很自然的想到,用deque作为底层容器来实现stack,queue,对于stack,queue的所有操作只需要对deque转调用即可

【STL源码剖析】第四章 序列式容器 之 deque底层实现 dequedeque概述vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操所。vector当然也可以在头尾端进行操作(从技术观点),但是其从头部操作效率奇差,无法被接受。deque和vector的最大差异,一在于deque允许常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓的容量概念,因为它... 阅读详情

相关推荐

innovus实战攻略 | 用setUsefulSkewMode解锁时序优化的“隐藏技能”!

就像时序优化的“瑞士军刀”,参数虽多,但掌握了分类逻辑后,用起来其实很简单!如果你有更绝的用法,欢迎在评论区分享,大家一起卷起来!简单来说,它就是时序优化的“乾坤大挪移”。通过调整触发器的时钟到达时间,把关键路径的建立时间“借”给非关键路径,从而实现全局时序收敛。别急,这个命令就是你的“时序救星”!(工具最多只能借1.2ns的裕量,防止“借过头”导致新问题!这个参数能帮你兜底!(对关键路径“下死手”,确保优化到位!(设置上限,给工具套上“紧箍咒”!(直接跳过边界,避免优化反噬!(全局管理,防止“借过头”!

weixin_50820814的博客 1354

_stl_deque中Iterator机制

_stl_deque中Iterator实现机制 Note:deque方便进行前增和后增,但是修改效率极低!如果要实现排序或其它操作,最好先复制到其它容器操作后再复制回 类中定义了typedef T** map_pointer,作为中继器,起到类似于块表的作用,保存指向各个分开存储的块的指针; Iterator中定义了成员函数map_pointer,cur_pointer,firs

so1ene的博客 545

麒麟linux离线源码编译安装zabbix-agent

离线源码编译安装zabbix-agent

qq_37263429的博客 2266

STL容器之deque

deque : 随机访问方便,即支持[ ] 操作符和vector.at() ,但性能没有vector 好; 可以在内部进行插入和删除操作,但性能不及list ; 可以在两端进行push 、pop ; 相对于verctor 占用更多的内存。 双向队列和向量很相似,但是它允许在容器头部快速插入和删除(就像在尾部一样)。 五个构造函数: deque();//创建一个空双向队列

阳光柠檬_的技术笔记 879

项目经理面试 5 大高频题 + 实战回答模板,帮你稳拿 offer

项目经理面试5大高频问题及应答技巧 摘要:本文总结了项目经理岗位常见的5个面试问题及回答策略,帮助求职者更有针对性地准备面试。核心问题包括:1)描述负责过的典型项目;2)常用项目管理工具;3)项目遇到的挑战及解决方案;4)风险管理方法;5)项目成功标准。每个问题都从面试官考察重点、回答技巧、实战案例三个维度进行分析,强调要用STAR法则(情境-任务-行动-结果)结构化表达,突出解决问题的能力而非简单罗列经历。特别提醒要量化项目成果(如提前2周上线、节省8%预算等),并注意展示商业思维和团队协作能力。

逸尘谈PM 2105

灵魂三问:因果推断

什么是因果推断?为什么研究因果推断?怎么进行因果推断研究?

ML OR 智能决策的博客 2902

C++ STL 迭代器(iterator)详解

指针可以用来遍历连续的数据结构,但是对于存储空间非连续的,就需要寻找一个行为类似指针的类,来对非数组的数据结构进行遍历。因此,我们引入迭代器概念。迭代器(Iterator)是一种检查容器内元素并遍历元素的数据类型。迭代器是指针的泛化,它允许程序员用相同的方式处理不同的数据结构(容器)。所有容器有含有其各自的迭代器型别(iterator types),所以当你使用一般的容器迭代器时,并不需要含入专门的头文件。不过有几种特别的迭代器,例如逆向迭代器,被定义于 中。

QtCompany的博客 2531

使用spacy规则化提取自然语言文本信息

1. 安装spacy pip install spicy spicy还需要载入文本库,使用pip的下载方式: python3 -m spacy download en_core_web_sm 但是很有可能因为网络问题下载速度非常缓慢,所以可以选择到github上去直接下载(注意和自己的spacy版本匹配):github下载链接 下载*.tar.gz文件即可。 然后切换到下载路径, pip install en_core_web_sm-3.1.0.tar.gz 2. spacy的一些基础用法 使用spa

六六六六神的博客 1818

STL源码刨析

1. STL概述 STL起源: 为的就是复用性的提升,减少人力资源的浪费,建立了数据结构与算法的一套标准。 STL所实现的、是依据泛型思维架设起来的一个概念结构。这个以抽象概念〔 abstract concepts)为主体而非以实际类(classes)为主体的结构,形成了一个严谨的接口标准。在此接口之下,任何组件都有最大的独立性,并以所谓迭代器〈 iterator)胶合起来,或以所谓配接器(adapter)互相配接,或以所谓仿函数( functor)动态选择某种策略( policy或strategy) 。

frighting_ing的博客 8263

避坑指南:Win10安装Docker Desktop时Hyper-V报错的7种解决方案(2024最新版)

本文提供了2024年最新、最全面的Windows 10系统下Docker Desktop安装时Hyper-V报错的解决方案指南。文章深入剖析了Hyper-V与Docker的共生关系,并系统性地给出了从BIOS/UEFI设置、Windows功能启用、bcdedit命令修复到处理多虚拟化环境共存等7种核心解决方案,帮助开发者彻底解决安装困局,顺利部署容器化开发环境。

weixin_28840143的博客 529

Realtek 10GbE芯片组解析:低成本高速网络方案

10GbE(万兆以太网)技术通过提升网络带宽,显著改善了数据传输效率,成为现代网络架构的核心组件。其工作原理基于高速串行通信协议,通过优化信号处理和编码技术实现低延迟、高吞吐量。在技术价值上,10GbE不仅满足数据中心和企业网络的高需求,还逐步渗透到消费级市场,推动家庭和小型企业的网络升级。应用场景包括NAS存储、视频编辑工作站和云服务接入等。Realtek最新发布的RTL8127、RTL8159和RTL8261C芯片组,以低功耗和低成本优势,进一步降低了10GbE的普及门槛。这些芯片支持PCIe Gen4

weixin_36427956的博客 583

STL源码剖析

全世界所有的STL实现版本,都源于Alexander Stepanov和Meng Lee完成的原始版本,这份原始版本有Hewlett-Packard Compant(惠普公司)拥有。每一个头文件都有一份声明,允许任何人任意运用、拷贝、修改、传播、贩卖这些代码,无需付费,唯一的条件是必须将声明置于使用者新开发的文件内这份开放源代码的精神,一般统称为GNU(音译为“革奴”),代码GUN isNotUnix。

NickDeCodes的博客 1728

科研——ICONIP论文修改和提交

ICONIP说明文档

Blackoutdragon的博客 6646

深入解剖STL deque:从源码剖析到容器适配器实现

✨本文深入剖析STL deque的底层实现原理,详细讲解其三层架构设计:中控器(map)、缓冲区(buffer)和复杂迭代器。通过源码分析揭示了deque如何实现头尾O(1)时间复杂度的插入删除操作,以及维护"整体连续"假象的技术细节。进一步探讨了基于deque的容器适配器实现,包括queue和priority_queue的设计原理与性能特点。文章最后提供各容器选型建议,帮助读者在实际开发中做出合理选择。掌握这些知识对于深入理解STL设计哲学和编写高效C++代码具有重要意义。

燃于AC之乐的博客 1319

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

【STL源码剖析】从源码看 deque :拆解双端队列的底层实现与核心逻辑

本文深入剖析了STL中deque的实现原理,主要面向熟悉STL底层机制的开发者。本文对比了vector和deque的结构差异,deque通过分段连续空间实现高效双向操作。重点解析了deque的两大核心组件:中控器(指针数组)负责管理各段空间,迭代器通过复杂设计维持++/--操作;解析deque整体连续的假象。详细介绍了迭代器的关键成员变量和跨越缓冲区的移动策略,包括++/--操作时的边界处理。通过源码分析展示了deque如何实现高效的内存管理和元素访问,为深入理解STL底层机制提供了宝贵参考。

2401_87944878的博客 2万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

STL 源码剖析 deque实现源码

#ifndef _HJSTL_DEQUE_H_ #define _HJSTL_DEQUE_H_ /* * Author:hujian * Time:2016/4/28 * discription:this file is about deque structure. * */ #include "hjstl_alloc.h" #include "hjstl_construct.h

hujian_的专栏 2477

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

《STL 源码剖析》 deque 实现原理

deque是双向开口的连续线性空间,而vector是单向开口的连续线性空间。deque没有容量的概念,它是动态地以分段连续空间组合而成,随时可以增加一点新的空间并链接起来。不会像 vector 因 “旧空间不足,而重新配置更大空间,然后复制旧元素,再释放旧空间”。因此deque 没有reserve功能。deque提供 Ramdon Access Iterator,但复杂度和vector不能相提并论,这也影响到各类算法方面。因此,除非必要,应尽量使用vector。

q1076452761的博客 908

STL deque 源码——deque特点、实现框架、源码分段剖析、常用函数总结(下)之源码剖析

源码剖析 // deque的特性: // 对于任何一个迭代器i // i.node是map array中的某元素的地址. i.node的内容是一个指向某个结点的头的指针 // i.first == *(i.node) // i.last == i.first + node_size // i.cur是一个指向[i.first, i.last)之间的指针 // 注意: 这意味着i.cur永远是一个可以解引用的指针, // 即使其是一

qq_37299596的博客 300

STL deque 源码——deque特点、实现框架、源码分段剖析、常用函数总结(上)

一、deque的一些特点 支持随机访问,即支持[ ]以及at(),但是性能没有vector好。 可以在内部进行插入和删除操作,但性能不及list。 deque 两端 都能够快速插入和删除元素,而vector只能在尾端进行。 deque的元素存取和迭代器操作会稍微慢一些,因为deque的内部结构会多一个间接过程。 deque迭代器是特殊的智能指针,而不是一般指针,它需要在不同的区块之间跳转。 deque可以包含更多的元素,其max_size可能更大,因为不止使用一块内存。 deque不支持对容量和内存分配时

qq_37299596的博客 463

STL 源码剖析:deque 的实现原理

虽然 deque 从表面上来说,它采用连续的空间,并且支持随机访问,即其迭代器为 random_access_iterators 类型。而且相比 vector,它还支持 push_front()。 // // ​ 但是实际上,deque 只是逻辑上的连续空间。它需要维护一个特殊的东西,叫做 deque 的中控器。该中控器主要由一个二级指针和一个 size 构成,(即可以理解为,该中控器是一个一级...

Error Man 的博客 488

《STL源码剖析》deque的实现

要点 和vector的差异: 1,双端开口--允许常数时间内对头端元素的插入/删除; 2,deque没有容量概念,因为它是动态以分段连续空间组合而成    不像vector那样”因为旧空间不足而重新配置一块更大空间,复制元素,释放空间“ deque: 1,一段一段的定量连续空间组成; 2,一旦在前端或尾端增加空间,配置一段定量连续空间,串接在整个deque的头或尾端; 3,在分

我的E家 906

STL源码剖析之stl_deque的实现(二)

STL中deque(双端队列)是一种非常重要的结构,stack,与queue底层都是借助deque. deque(双端队列)是一种双向开口的连续性空间,双向开口意思是可以在头尾两端做元素的插入和删除操作。deque和vector最大的差异在于deque允许常数时间内对起头端进行元素的插入或移除操作,二者在于deque没所谓的容量(capacity)观念,因为它是动态地以分段连续空间组合而成,随时...

zzb2019的博客 268

deque底层实现\栈与队列的实现——STL源码剖析阅读笔记

(头部则是会判断没有备用空间),如果有两个以上备用空间,那么我们可以直接讲元素放入缓冲区,但是若是没有,则会触发push_back_aux()函数。这个函数首先会判断。

qq_49394155的博客 165

STL源码剖析(3):deque,以及C/C++下的stack,queue实现

接下来咱们来看看deque deque deque,简称双端队列,顾名思义,就是两端都可以进行进出操作,即双向开口的连续线性空间。 vector当然也可以在头尾两端进行操作,但是其头部操作效率奇差,无法被接受 deque的中控器 deque系由一段定量连续空间组成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接deque的头端额尾端。换来的是极其复杂的迭代器。 deque采用一块所谓的map(不是map容器)作为主控,是 一小块连续空间,其中每个元素都是指针,指向另一端的连续空间

遇事不决,问春风 558
上一篇: STL源码剖析——type traits编程技法
下一篇: =default, =delete
Coding_Reading
博客等级 码龄10年 17粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值