C++ 全栈知识体系C++ 全栈知识体系
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
  • 编程语言与技术

    • Effective C++: 改善程序与设计的55个具体做法

      • 第2章 - 构造/析构/赋值运算(一)
      • 第2章 - 构造/析构/赋值运算(二)
      • 第2章 - 构造/析构/赋值运算(三)
      • 第3章 - 资源管理
      • 第4章 - 设计与声明(一)
      • 第4章 - 设计与声明(二)
      • 第5章 - 实现(一)
      • 第5章 - 实现(二)
      • 第6章 - 继承与面向对象设计
      • 第7章 - 模板与泛型编程
    • 深度探索C++对象模型

      • 第1章 - 关于对象
      • 第2章 - 构造函数语意学
      • 第3章 - Data 语意学
    • STL源码剖析

      • 第1章 - STL概论和版本简介
      • 第2章 - 空间配置器
      • 第3章 - 迭代器(iterators)概念与traits编程技法(一)
      • 第3章 - 迭代器(iterators)概念与traits编程技法(二)
      • 第4章 - 序列式容器 vector
      • 第4章 - 序列式容器 list
      • 第4章 - 序列式容器 deque
      • 第4章 - 序列式容器 stack和queue
      • 第4章 - 序列式容器 heap
      • 第4章 - 序列式容器 priority_queue
      • 第4章 - 序列式容器 slist
      • 第5章 - 关联式容器 RB-tree
      • 第5章 - 关联式容器 set和map
      • 第5章 - 关联式容器 hashtable
      • 第6章 - 算法
      • 第6章 - 算法之set
      • 第7章 - 仿函数
      • 第8章 - 配接器
  • 系统与架构

    • 深入理解计算机系统

      • 第1章 - 计算机系统漫游
      • 第2章 - 信息的表示和处理
      • 第3章 - 程序的机器级表示
      • 第5章 - 优化程序性能
      • 第6章 - 存储器层次结构
      • 第7章 - 链接
      • 第8章 - 异常控制流
      • 第9章 - 虚拟内存
      • 第10章 - 系统级I/O
      • 第11章 - 网络编程
      • 第12章 - 并发编程
    • 大型网站技术架构——核心原理与案例分析

      • 第1章 - 大型网站架构演化
      • 第2章 - 大型网站架构模式
      • 第3章 - 大型网站核心架构要素
      • 第4章 - 瞬时响应:网站的高性能架构
      • 第5章 - 万无一失:网站的高可用架构
      • 第6章 - 永无止境:网站的伸缩性架构
      • 第7章 - 随需应变:网站的可扩展架构
      • 第8章 - 固若金汤:网站的安全架构
    • 从零开始学架构

      • 架构基础
      • 架构设计原则
      • 高性能架构
      • 高可用架构
    • 程序员的自我修养————链接、装载与库

      • 第1章 - 简介
      • 第2章 - 静态链路
      • 第3章 - 目标文件里有什么
      • 第4章 - 静态链接
      • 第7章 - 动态链接
      • 第8章 - 共享库版本
      • 第10章 - 内存
      • 第11章 - 运行库
      • 第12章 - 系统调用与API
      • 第13章 - 运行库实现
  • 软件开发实践

    • 重构改善既有代码的设计

      • 第1章 - 重构,第一个示例
      • 第2章 - 重构的原则
      • 第3章 - 代码的坏味道
      • 第5章 - 重构列表
      • 第6章 - 重新组织函数
      • 第7章 - 在对象之间搬移特性
      • 第8章 - 重新组织数据
      • 第9章 - 简化条件表达式
      • 第10章 - 简化函数调用
      • 第11章 - 处理概括关系
      • 第12章 - 设计之大型重构
    • 代码大全2

      • 第1章 - 欢迎进入软件构建的世界
      • 第2章 - 用隐喻来更充分地理解软件开发
      • 第3章 - 三思而后行: 前期准备
      • 第4章 - 关键的构建决策
      • 第5章 - 软件构建中的设计
    • Linux多线程服务端编程——使用muduo C++ 网络库

      • Buffer类的设计
      • 设计与实现
      • 定时器与TimerQueue
      • Protobuf网络传输和Protobuf编解码器与消息分发器
      • EventLoop类剖析
      • EventLoopThread和EventLoopThreadPool剖析
      • TCP网络库和核心类
      • Connector剖析
      • TcpClient剖析
      • 学习总结
      • timing wheel
      • 消息广播服务
      • 线程安全的对象生命期管理
  • 数据处理与应用设计

    • 数据密集型应用系统设计

      • 第1章 - 可靠、可扩展与可维护的应用系统
      • 第2章 - 数据模型与查询语言
      • 第3章 - 数据存储与检索
      • 第4章 - 数据编码与演化
      • 第5章 - 数据复制
      • 第6章 - 数据分区
      • 第7章 - 事务

第4章:序列式容器 deque

    deque概述

    deque(double-ended queue,双端队列)是一种具有队列和栈的性质的数据结构。相比于vector单向开口的连续线性空间而言,deque则是一种双向开口的连续线性空间,可以在头尾两端分别做元素的插入和删除操作。虽然vector从技术层面也可以对头部操作,但是效率极低。即deque与vector的最大差异在于:

    1. deque可以在常数时间内完成对头部元素的插入或删除操作;
    2. deque没有容量的概念,它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。

    deque

    deque的中控器

    deque由一段一段的定量连续空间构成。一旦有必要在dequer前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque的最大任务是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器架构。

    deque采用一块所谓的map作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。

    template<class T, class Alloc = alloc, size_t BufSiz = 0>
    class deque{
    public :
    	typedef	T value_type ;
    	typedef	value_type* pointer ;
    	...
    protected :
    	//元素的指针的指针(pointer of pointer of T)
    	typedef	pointer* map_pointer ; //其实就是T**
     
    protected :
    	map_pointer map ; //指向map,map是块连续空间,其内的每个元素
    					  //都是一个指针(称为节点),指向一块缓冲区
    	size_type map_size ;//map内可容纳多少指针
    	...
    };
    

    deque中控器

    deque的迭代器

    deque是分段连续空间,维持”整体连续“假象的任务,落在迭代器的operator++和operator—两个运算子上。

    template<class T, class Ref, class Ptr, size_t BufSiz>
    struct __deque_iterator{ //未继承std::iterator
    	typedef	__deque_iterator<T,T&,T*,BufSize>	iterator ;
    	typedef	__deque_iterator<T,const T&,const T*,BufSize>	const_iterator ;
    	static	size_t	buffer_size() {return __deque_buf_size(BufSize,sizeof(T)) ;} 
     
    	//未继承std::iterator,所以必须自行撰写五个必要的迭代器相应型别
    	typedef	random_access_iterator_tag	iterator_category ;
    	typedef	T	value_type ;
    	typedef	Ptr	pointer ;
    	typedef	Ref	reference ;
    	typedef	size_t	size_type ;
    	typedef	ptrdiff_t	difference_type ;
    	typedef	T**	map_pointer ;
     
    	typedef	__deque_iterator	self ;
     
    	//保持与容器的联结
    	T *cut ; //此迭代器所指之缓冲区中的现行(current)元素
    	T *first ; //此迭代器所指之缓冲区的头
    	T *last ;	//此迭代器所指之缓冲区的尾(含备用空间)
    	map_pointer node ; //指向管控中心
    	...
    };
    

    deque中控器、缓冲区、迭代器的相互关系

    deque的数据结构

    deque除了维护一个指向map的指针外,也维护start,finish两个迭代器,分别指向第一缓冲区的第一个元素和最后缓冲区的最后一个元素(的下一个位置)。

    template<class T, class Alloc = alloc, size_t BufSiz = 0>
    class deque{
    public :
    	typedef	T	value_type ;
    	typedef	value_type*	pointer ;
    	typedef	size_t	size_type ;
    public :
    	typedef	__deque_iterator<T,T&,T*,BufSiz>	iterator ;
    protected :
    	//元素的指针的指针(pointer of pointer of T)
    	typedef	pointer*	map_pointer ;
    protected:
    	iterator	start ; //表现第一节点
    	iterator	finish ; //表现最后一个节点
    	map_pointer	map ; //指向map,map是块连续空间,其每个元素都是个指针,指向一个节点(缓冲区)
    	size_type	map_size ; //map内有多少指针
    	...
    } ;
    

    deque的构造与内存管理

    以程序实现来初步了解deque的构造和内存管理:

    #include <deque>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    class alloc {
    };
    
    int main() {
        deque<int, allocator<int>> ideq(20, 9);
    	//deque<int,alloc,8> ideq(20, 9);//在linux下不支持alloc
        cout << "size=" << ideq.size() << endl;
    
        for (int i = 0; i < ideq.size(); ++i)
            cout << ideq[i] << ' ';
        cout << endl;
    
        for (int i = 0; i < 3; ++i)
            ideq.push_back(i);
        
        for (int i = 0; i < ideq.size(); ++i)
            cout << ideq[i] << ' ';
        cout << endl;
        cout << "size=" << ideq.size() << endl;
    
        ideq.push_back(3);
        for (int i = 0; i < ideq.size(); ++i)
            cout << ideq[i] << ' ';
        cout << endl;
        cout << "size=" << ideq.size() << endl;
    
        ideq.push_front(99);
        for (int i = 0; i < ideq.size(); ++i)
            cout << ideq[i] << ' ';
        cout << endl;
        cout << "size=" << ideq.size() << endl;
    
        ideq.push_front(98);
        ideq.push_front(97);
        for (int i = 0; i < ideq.size(); ++i)
            cout << ideq[i] << ' ';
        cout << endl;
        cout << "size=" << ideq.size() << endl;
    
        deque<int, allocator<int>>::iterator itr;
        itr = find(ideq.begin(), ideq.end(), 99);
        cout << *itr << endl;
        cout << *(itr._M_cur) << endl;
    }
    

    执行结果:

    [root@192 4_STL_sequence_container]# ./4_4_5_deque-test                         
    size=20
    9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
    9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 1 2
    size=23
    9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 1 2 3
    size=24
    99 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 1 2 3
    size=25
    97 98 99 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 1 2 3
    size=27
    99
    99
    

    上述程序的处理过程可以表现为如下图像:

    deque-1

    deque-2

    deque-3

    deque-4

    deque的构造和析构函数:

      deque():start(),finish(),map_size(0),map(0)
      {
          create_map_and_nodes(0);
      }
      deque(int n,const value_type& value):start(),finish(),map(0),map_size(0)
      {
          fill_initialize(n,value);
      }
      ~deque(){}
    
      void fill_initialize(size_type n,const value_type& value)
      { //负责产生并安排好deque的结构,并将元素的初值设定好
            create_map_and_nodes(n); //把deque的结构都安排好
            map_pointer cur;
            //已经获得空间,为每个节点缓冲区设定初值
            for(cur=start.node;cur<finish.node;++cur)
            {
                uninitialized_fill(*cur,*cur+buffer_size(),value);
            }
            //最后一个节点的设定稍有不同(尾端可能有备用空间,不必设初值)
            uninitialized_fill(finish.first,finish.cur,value);
        }
    
        void creat_map_and_nodes(size_type num_elements)
        { //产生并安排好deque的结构
            size_type num_nodes=num_elements/buffer_size()+1; 
            //一个map要管理几个节点,最少8个,最多是“所需节点数+2”,前后各预留一个,扩充时用
            map_size=max(initial_map_size(),num_nodes+2);
            map=map_allocator::allocate(map_size);
    
            map_pointer nstart=map+(map_size-num_nodes)/2;
            map_pointer nfinish=nstart+num_nodes-1;
            map_pointer cur;
    
            for(cur=nstart;cur<=nfinish;cur++)
            {
                *cur=allocate_node();
            }
            start.set_node(nstart);
            finish.set_node(nfinish);
            start.cur=start.first;
            finish.cur=finish.first+num_elements%buffer_size();
        }
    

    deque的元素操作

    void push_back(const value_type &t)
    {
        if (finish.cur != finish.last - 1)
        {
            construct(finish.cur, t);
            ++finish.cur;
        }
        else //需要配置新的缓冲区
        {
            push_back_aux(t);
        }
    }
    
    void push_front(const value_type &t)
    {
        if (start.cur != start.first)
        {
            construct(start.cur - 1, t);
        }
        else
        {
            push_front_aux(t);
        }
    }
    
    void pop_back()
    {
        if (finish.cur != finish.first)
        {
            --finish.cur;
            destroy(finish.cur); //将最后元素析构,左开右闭
        }
        else
        {
            //最后缓冲区没有任何元素
            pop_back_aux(); //这里将进行缓冲区的释放工作
        }
    }
    
    void pop_front()
    {
        if (start.cur != start.last - 1)
        {
            destroy(start.cur);
            ++start.cur;
        }
        else
            pop_front_aux();
    }
    
    void push_back_aux(const value_type &t) //只剩最后一个缓冲区的最后一个备用缓冲区
    {                                       //先配置一块新的缓冲区,再设新元素内容,更改迭代器finish的状态
        value_type t_copy = t;
        reserve_map_at_back();                //若符合某种条件,则必须重换一个map
        *(finish.node + 1) = allocate_node(); //配置一新缓冲区
        construct(finish.cur, t_copy);
        finish.set_node(finish.node + 1);
        finish.cur = finish.first;
    }
    
    void push_front_aux(const value_type &t)
    {
        value_type t_copy = t;
        reserve_map_at_front();
        *(start.node - 1) = allocate_node();
        start.set_node(start.node - 1);
        start.cur = start.last - 1;
        construct(start.cur, t_copy);
    }
    
    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);
        }
    }
    
    void reserve_map_at_front(size_type nodes_to_add = 1)
    {
        if (nodes_to_add > start.node - map)
        {
            reallocate_map(nodes_to_add, true);
        }
    }
    
    
    void pop_back_aux()
    {                                  //finish.cur==finish.first 释放该缓冲区
        deallocate_node(finish.first); //释放最后一个缓冲区
        finish.set_node(finish.node - 1);
        finish.cur = finish.last - 1;
        destroy(finish.cur); //析构
    }
    
    void pop_front_aux()
    {
        destory(start.cur);
        deallocate_node(start.last);
        start.set_node(start.node + 1);
        start.cur = start.first;
    }
    
    void clear()
    {
        for (map_pointer node = start.node + 1; node < finish.node; ++node)
        {
            destory(*node, *node + buffer_size());            //析构元素
            data_allocator::deallocate(*node, buffer_size()); //释放内存
        }
    
        if (start.node != finish.node) //至少有两个缓冲区
        {
            destroy(start.cur, start.last);
            destroy(finish.first, finish.cur);
            //保留头缓冲区
            data_allocator::deallocate(finish.first, buffer_size());
        }
        else
        {
            destroy(start.cur, finish.cur);
        }
        finish = start;
    }
    
    iterator begin() { return start; }
    iterator end() { return finish; }
    
    reference operator[](size_type n)
    {
        //调用_deque_iterator<>::operator[]
        return *(start + n);
    }
    
    reference front() { return *start; }
    reference back() { return *(finish - 1); }
    
    size_type size() const { return finish - start; }
    size_type max_size() const { return size_type(-1); }
    
    bool empty() const { return finish == start; }
    
    Last Updated:
    Contributors: klc407073648
    Prev
    第4章 - 序列式容器 list
    Next
    第4章 - 序列式容器 stack和queue