转自:https://www.cnblogs.com/LearningTheLoad/p/7450948.html

deque双向开口可进可出的容器

我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去

deque却可以,它创造了内存连续的假象.

其实deque由一段一段构成 ,他是分段连续,而不是内存连续 当走向段的尾端时候自动跳到下一段 所以支持迭代器++ 操作,自动跳到下一段的方法由operator++实现

deque每次扩充 申请一个段

一 定义

头文件 #include <deque>

int main_0() {

    //默认构造函数 创建一个空的deque
deque<int> c; //拷贝构造
deque<int> c1(c); //赋值拷贝
deque<int> c2 = c1; //指定元素个数创建
deque<int> c3 (5,6);
for (auto i : c3) {
cout<< i << ",";
}
cout << "deque(个数, 元素)"<<endl; //指定区间创建
deque<int> c4(c3.begin()+2, c3.begin()+3);
for (auto i : c4) {
cout<< i << ",";
}
cout << "deque(区间, 区间)"<<endl; //指定初始化列表创建
deque<int> c5({2,3,4,5});
for (auto i : c5) {
cout<< i << ",";
}
cout << "deque({})"<<endl; //指定初始化列表创建
deque<int> c6 = {2,3,4,5};
for (auto i : c6) {
cout<< i << ",";
}
cout << "deque = {}" <<endl;
cout<<endl;
return 0;
}

二 操作

int main()
{
deque<int> c = {1,2,3,4,5};
deque<int> c1; // 赋值初始化
c1.assign(c.begin(),c.end());
for (auto i: c1) {
cout<< i << ",";
}
cout << "assign()" <<endl; //在尾部插入
c1.push_back(6);
for (auto i: c1) {
cout<< i << ",";
}
cout << "push_back()" <<endl; //头插入
c1.push_front(0);
for (auto i: c1) {
cout<< i << ",";
}
cout << "push_front()" <<endl; //弹尾元素
c1.pop_back();
for (auto i: c1) {
cout<< i << ",";
}
cout << "pop_back()" <<endl; //弹头元素
c1.pop_front();
for (auto i: c1) {
cout<< i << ",";
}
cout << "pop_front()" <<endl; //指定位置插入元素
c1.insert(c1.begin()+3, 10);
for (auto i: c1) {
cout<< i << ",";
}
cout << "insert()" <<endl; //删除指定位置元素
c1.erase(c1.begin()+3);
for (auto i: c1) {
cout<< i << ",";
}
cout << "erase()" <<endl; //清空deque
c.clear();
for (auto i: c) {
cout<< i << ",";
}
cout << "clear()" <<endl; //构造
c1.emplace(c1.end(), 100);
for (auto i: c1) {
cout<< i << ",";
}
cout << "emplace()" <<endl; //头位置插入元素
c1.emplace_front(111);
for (auto i: c1) {
cout<< i << ",";
}
cout << "emplace_front()" <<endl; //尾位置插入元素
c1.emplace_back(111);
for (auto i: c1) {
cout<< i << ",";
}
cout << "emplace_back()" <<endl; //交换
c.swap(c1);
for (auto i: c) {
cout<< i << ",";
}
cout << "swap()" <<endl; int tmp;
tmp = c.front();
cout<<"第一个元素"<< tmp << endl; tmp = c.back();
cout<<"最后一个元素"<< tmp << endl; tmp = c.at(1);
cout<<"指定下标元素"<< tmp << endl; tmp = c[1];
cout<<"指定[]下标元素"<< tmp << endl; return 0;
}

三 内存

int main()
{
deque<int> c = {1,2,3,4,5}; //元素个数
cout<< "size(): " << c.size() <<endl; //重新设定deque大小 少退多补
c.resize(10, 5);
for(auto i : c)
{
cout << i <<",";
}
cout << "resize()" << endl; //最大容量
cout << "max_size(): " << c.max_size() <<endl; //是否为空
cout << "empty: " << c.empty() << endl; //清空内存
c.shrink_to_fit(); return 0;
}

五 与迭代器操作

int main()
{
deque<int> c = {1,2,3,4,5};
//指向第一个元素的迭代器
cout << *c.begin() << endl;
//指向最后一个元素的下一个位置的迭代器
cout << *c.end() << endl;
//反向deque的第一个元素的迭代器
cout << *c.rbegin() << endl;
//反向deque的第一个元素的上一个位置迭代器
cout << *c.rend() << endl;
//指向第一个元素的迭代器
cout << *c.cbegin() << endl;
//指向最后一个元素的下一个位置的迭代器
cout << *c.cend() << endl;
//反向deque的第一个元素的迭代器
cout << *c.crbegin() << endl;
//反向deque的第一个元素的上一个位置迭代器
cout << *c.crend() << endl; return 0;
}

Soource Code

deque的类图如下,跟vector的类图结构相似

_Deque_base<_Tp>基类

    template<typename _Tp, typename _Alloc>
class _Deque_base
{
...
struct _Deque_impl
: public _Tp_alloc_type
{
_Tp** _M_map;
size_t _M_map_size;
iterator _M_start;
iterator _M_finish;
}
...
}

_Deque_impl<_Tp>数据类

成员变量含义,如下图所示

_M_map指向存储  指向内存的指针  的连续内存

_M_map_size表示_M_map指向的内存大小(有多少个buffer)

_M_start指向_M_map指向内存起始点的迭代器

_M_finish指向_M_map指向内存结束点的迭代器

deque<_Tp>

    template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class deque : protected _Deque_base<_Tp, _Alloc>
{
//成员变量含义与vector成员变量含义相似
public:
typedef _Tp value_type;
typedef typename _Tp_alloc_type::pointer pointer;
typedef typename _Tp_alloc_type::const_pointer const_pointer;
typedef typename _Tp_alloc_type::reference reference;
typedef typename _Tp_alloc_type::const_reference const_reference;
typedef typename _Base::iterator iterator;
typedef typename _Base::const_iterator const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Alloc allocator_type; ...
//这个函数返回buffer大小
static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT
{ return __deque_buf_size(sizeof(_Tp)); } } //它的实现如下
#ifndef _GLIBCXX_DEQUE_BUF_SIZE
#define _GLIBCXX_DEQUE_BUF_SIZE 512
#endif inline size_t
__deque_buf_size(size_t __size)
{ return (__size < _GLIBCXX_DEQUE_BUF_SIZE
? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1)); }
/*
如果是deque<int>的话 上面的函数可以翻译成 4 < 512 ? size_t(512/4) " size_t(1)
结果就是512/4 = 128
那么每个buffer所含value_type的个数就128,即一个buffer能存128个int deque<double> 的话就是512/8 = 64 那么每个buffer的所含个数就是64 如果>=512 就是每个buffer只能存1个 (class/array/struct)
*/

解析一个成员函数insert(),在解析之前介绍一下它的迭代器成员变量的含义

start 指向头的迭代器(_M_start)

finish 指向尾部的下一个位置的迭代器(_M_finish)

cur 指向当前元素

first 指向当前buffer内的第一个元素

last 指向单钱buffer的最后一个元素

node 指向当前buffer的指针

map_size node的个数

     insert(const_iterator __position, value_type&& __x)
{ return emplace(__position, std::move(__x)); }

empalce()

     template<typename _Tp, typename _Alloc>
template<typename... _Args>
typename deque<_Tp, _Alloc>::iterator
deque<_Tp, _Alloc>::
emplace(const_iterator __position, _Args&&... __args)
{
if (__position._M_cur == this->_M_impl._M_start._M_cur) //插入的位置是否在最前端
{
emplace_front(std::forward<_Args>(__args)...);
return this->_M_impl._M_start;
}
else if (__position._M_cur == this->_M_impl._M_finish._M_cur)//插入的位置在最尾端
{
emplace_back(std::forward<_Args>(__args)...);
iterator __tmp = this->_M_impl._M_finish;
--__tmp;//迭代器指向的是最尾端的下一个位置
return __tmp;
}
else
return _M_insert_aux(__position._M_const_cast(),
std::forward<_Args>(__args)...);
}

emplace_front()

 template<typename _Tp, typename _Alloc>
template<typename... _Args>
void
deque<_Tp, _Alloc>::
emplace_front(_Args&&... __args)//插入在最前端
{
if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first)//如果最前端的buffer内存够的话
{
this->_M_impl.construct(this->_M_impl._M_start._M_cur - 1,
std::forward<_Args>(__args)...);//直接在备用空间上构造元素
--this->_M_impl._M_start._M_cur;//调整cur
}
else //如过内存不够
_M_push_front_aux(std::forward<_Args>(__args)...);
} #if __cplusplus >= 201103L
template<typename... _Args>
void
deque<_Tp, _Alloc>::
_M_push_front_aux(_Args&&... __args)
#else
void
deque<_Tp, _Alloc>::
_M_push_front_aux(const value_type& __t)
#endif
{
_M_reserve_map_at_front();
*(this->_M_impl._M_start._M_node - 1) = this->_M_allocate_node();//申请空的头结点
__try
{
this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node
- 1); //改变start,令其指向新节点
this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - 1; ////改变cur,
#if __cplusplus >= 201103L
this->_M_impl.construct(this->_M_impl._M_start._M_cur,
std::forward<_Args>(__args)...);//复制
#else
this->_M_impl.construct(this->_M_impl._M_start._M_cur, __t);
#endif
}
__catch(...)
{
++this->_M_impl._M_start;
_M_deallocate_node(*(this->_M_impl._M_start._M_node - 1));
__throw_exception_again;
}
}

_M_insert_aux

#if __cplusplus >= 201103L
template<typename... _Args>
typename deque<_Tp, _Alloc>::iterator
deque<_Tp, _Alloc>::
_M_insert_aux(iterator __pos, _Args&&... __args)
{
value_type __x_copy(std::forward<_Args>(__args)...); // XXX copy
#else
typename deque<_Tp, _Alloc>::iterator
deque<_Tp, _Alloc>::
_M_insert_aux(iterator __pos, const value_type& __x)
{
value_type __x_copy = __x; // XXX copy
#endif
difference_type __index = __pos - this->_M_impl._M_start;//重在operator- 返回距起始端距离 安插点之前的元素个数
if (static_cast<size_type>(__index) < size() / 2)//若果插入点在整体内存的前半段,插入后整体内存前移
{
push_front(_GLIBCXX_MOVE(front()));//复制头buffer的一个元素
iterator __front1 = this->_M_impl._M_start;//取出头迭代器位置,和下一个节点的起始点
++__front1;
iterator __front2 = __front1;
++__front2;
__pos = this->_M_impl._M_start + __index;
iterator __pos1 = __pos;
++__pos1;
_GLIBCXX_MOVE3(__front2, __pos1, __front1);//front1 到 front2 这段区间内的前pos1个元素向前移动一个单位 (留出一个单位给将插入的的元素)
}
else
{
push_back(_GLIBCXX_MOVE(back()));
iterator __back1 = this->_M_impl._M_finish;
--__back1;
iterator __back2 = __back1;
--__back2;
__pos = this->_M_impl._M_start + __index;
_GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1);
}
*__pos = _GLIBCXX_MOVE(__x_copy); //复制元素
return __pos;
}

既然说deque是模仿内存连续,实现这种功能的主要功臣是deque的迭代器,说道迭代器 那么我们一定会联想到迭代器的操作符重载,下面我主要介绍迭代器的操作符重载

operator* 这个比较好理解 返回cur指向的元素

        reference
operator*() const _GLIBCXX_NOEXCEPT
{ return *_M_cur; } pointer
operator->() const _GLIBCXX_NOEXCEPT
{ return _M_cur; }

operator++()(前++)与operator++(int)

        _Self&
operator++() _GLIBCXX_NOEXCEPT
{
++_M_cur;
if (_M_cur == _M_last)//如果++到达该节点尾端
{
_M_set_node(_M_node + 1);//跳至下一个节点
_M_cur = _M_first;
}
return *this;
} _Self
operator++(int) _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
++*this;//调用前++
return __tmp;
}

operator--()与operator--(int)

        _Self&
operator--() _GLIBCXX_NOEXCEPT
{
if (_M_cur == _M_first)//如果--后到达头端
{
_M_set_node(_M_node - 1);//跳至前一个节点
_M_cur = _M_last;
}
--_M_cur;
return *this;
} _Self
operator--(int) _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
--*this;
return __tmp;
}

operator+=(itn) ,+,-=,-

        _Self&
operator+=(difference_type __n) _GLIBCXX_NOEXCEPT
{
const difference_type __offset = __n + (_M_cur - _M_first);//查找位置
if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))//查找位置是否小于一个buffer的容量(在当前buffer内)
_M_cur += __n;
else
{
const difference_type __node_offset =
__offset > 0 ? __offset / difference_type(_S_buffer_size())
: -difference_type((-__offset - 1)
/ _S_buffer_size()) - 1;
_M_set_node(_M_node + __node_offset);//切换至正确buffer
_M_cur = _M_first + (__offset - __node_offset
* difference_type(_S_buffer_size()));
}
return *this;
} _Self
operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
return __tmp += __n;//调用+=
} _Self&
operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
{ return *this += -__n; }//调用+= _Self
operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
return __tmp -= __n;
}

operator[],和_M_set_node

        reference
operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
{ return *(*this + __n); } /**
* Prepares to traverse new_node. Sets everything except
* _M_cur, which should therefore be set by the caller
* immediately afterwards, based on _M_first and _M_last.
*/
void
_M_set_node(_Map_pointer __new_node) _GLIBCXX_NOEXCEPT//切换至正确buffer
{
_M_node = __new_node;
_M_first = *__new_node;
_M_last = _M_first + difference_type(_S_buffer_size());
}

参考<<侯捷STL标准库>>

deque的更多相关文章

  1. C++ std::deque

    std::deque template < class T, class Alloc = allocator > class deque; Double ended queue deque ...

  2. collections 模块(namedtuple, deque, Counter )

    基本介绍 我们都知道,Python拥有一些内置的数据类型,比如str, int, list, tuple, dict等, collections模块在这些内置数据类型的基础上,提供了几个额外的数据类型 ...

  3. vector、list、deque三者比较

    1.vector是一段连续的内存块,而deque是多个连续的内存块,list是所有数据元素分开保存,可以是任何两个元素都没有连续. 2.vector的查询性能最好,并且的末端增加数据也很好,除非它重新 ...

  4. STL之deque

    deque是一种优化了的,对序列两段进行添加和删除操作的基本序列容器.它允许较为快速的随机访问,但它不像vector把所有对象保存在一块连续的内存块,而是采用多个连续的存储块.向deque两段添加或删 ...

  5. Deque的部分成员函数 解析,关于这个类,百度有很多解析,唯独没有其函数介绍

    函数 描述 c.assign(beg,end) c.assign(n,elem) 将[beg; end)区间中的数据赋值给c. 将n个elem的拷贝赋值给c. c.at(idx) 传回索引idx所指的 ...

  6. Python_Day_05 计数器(counter),有序字典(OrderDict),默认字典(defaultdict),可命名元祖(namedtuple),双向队列(deque),单项队列(deuqe.Queue)

    Counter(计数器) 是一个字典的子类,存储形式同样为字典,其中存储的键为字典的元素,值为元素出现的次数,在使用之前我们需要先导入文件 import collections 初始化一个计数器 im ...

  7. 计数器(counter),有序字典(OrderDict),默认字典(defaultdict),可命名元祖(namedtuple),双向队列(deque),单项队列(deuqe.Queue)

    Python_Day_05 计数器(counter),有序字典(OrderDict),默认字典(defaultdict),可命名元祖(namedtuple),双向队列(deque),单项队列(deuq ...

  8. 利用python的双向队列(Deque)数据结构实现回文检测的算法

    #!/usr/bin/env python # -*- coding: utf-8 -*- # learn <<Problem Solving with Algorithms and Da ...

  9. Python强化训练笔记(七)——使用deque队列以及将对象保存为文件

    collections模块中的deque对象是一个队列,它有着正常队列的先进先出原则.我们可以利用这个对象来实现数据的保存功能. 例如,现有一个猜数字大小的游戏,系统开始会随机roll点一个0-100 ...

  10. STL之序列容器deque

    首先看看deque的模板声明: template <class T,  class Alloc = allocator<T>>  // 原本还有个参数BufSize,现在新版本 ...

随机推荐

  1. 洛谷P1173 [NOI2016]网格

    这个码量绝对是业界大毒瘤...... 300行,6.5k,烦的要死...... 题意:给你一个网格图,里面有0或1.你需要把一些0换成1使得存在某两个0不四联通.输出最小的换的数量.无解-1. n,m ...

  2. c# 获取机器硬件信息 (硬盘,cpu,内存等)

    using System; using System.Collections.Generic; using System.Globalization; using System.Management; ...

  3. 代码合并工具Beyond Compare的使用技巧

    使用技巧 文件合并 过滤 https://edi.wang/post/2013/2/17/using-beyond-compare 文件夹比较 http://www.beyondcompare.cc/ ...

  4. echarts 调整图表大小的方法

    第一次使用Echarts,大小用的不是那么随心应手,通过文档和百度出的结果,发现其实很简单: 内部图表大小是与div容器大小相关的,如果想调整图表大小,调整div就可以了 如果是想调整图表与div间上 ...

  5. 关于处理iis8.0中设置Request.BinaryRead 不允许操作的解决方法

    iis6.0解决方案: 起初我刚开始上传的是小文件运行都是正常的,后来我弄个文件大点的上传看程序运行怎么样?就上面的问题,在网上搜索正好找到跟我一样的问题,拿过来自己记录下.其中行62指的是:oUpF ...

  6. BSGS与exBSGS学习笔记

    \(BSGS\)用于解决这样一类问题: 求解\(A^x ≡B(modP)\)的最小\(x\),其中\(P\)为质数. 这里我们采用分块的方法,把\(x\)分解为\(i *t-b\)(其中\(t\)是分 ...

  7. mysql主从模式下在主库上的某些操作不记录日志的方法

    mysql主从模式下在主库上的某些操作不记录日志的方法 需求场景: 在主库上的需要删除某个用户,而这个用户在从库上不存在(我在接手一个业务的时候,就遇到主从架构用户授权不一致的情况,主库比较全,而从库 ...

  8. python操作execl学习笔记(一)

    本节只记录关于execl的读操作: execl 内容及格式 python3 #!/usr/bin/env python #-*- coding:utf-8 -*- import xlrd import ...

  9. hive metastore && hiveserver2 . 基本配置

    <?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-s ...

  10. Silver 操作Cookie

    public class CookiesUtils { public static void SetCookie(String key, String value) { SetCookie(key, ...