技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性。

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 (,);
for (auto i : c3) {
cout<< i << ",";
}
cout << "deque(个数, 元素)"<<endl; //指定区间创建
deque<int> c4(c3.begin()+, c3.begin()+);
for (auto i : c4) {
cout<< i << ",";
}
cout << "deque(区间, 区间)"<<endl; //指定初始化列表创建
deque<int> c5({,,,});
for (auto i : c5) {
cout<< i << ",";
}
cout << "deque({})"<<endl; //指定初始化列表创建
deque<int> c6 = {,,,};
for (auto i : c6) {
cout<< i << ",";
}
cout << "deque = {}" <<endl;
cout<<endl;
return ;
}

二 操作

int main()
{
deque<int> c = {,,,,};
deque<int> c1; // 赋值初始化
c1.assign(c.begin(),c.end());
for (auto i: c1) {
cout<< i << ",";
}
cout << "assign()" <<endl; //在尾部插入
c1.push_back();
for (auto i: c1) {
cout<< i << ",";
}
cout << "push_back()" <<endl; //头插入
c1.push_front();
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()+, );
for (auto i: c1) {
cout<< i << ",";
}
cout << "insert()" <<endl; //删除指定位置元素
c1.erase(c1.begin()+);
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(), );
for (auto i: c1) {
cout<< i << ",";
}
cout << "emplace()" <<endl; //头位置插入元素
c1.emplace_front();
for (auto i: c1) {
cout<< i << ",";
}
cout << "emplace_front()" <<endl; //尾位置插入元素
c1.emplace_back();
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();
cout<<"指定下标元素"<< tmp << endl; tmp = c[];
cout<<"指定[]下标元素"<< tmp << endl; return ;
}

三 内存

int main()
{
deque<int> c = {,,,,}; //元素个数
cout<< "size(): " << c.size() <<endl; //重新设定deque大小 少退多补
c.resize(, );
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 ;
}

五 与迭代器操作

int main()
{
deque<int> c = {,,,,};
//指向第一个元素的迭代器
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 ;
}

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()); }
/*
如果是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 - ,
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 - ) = this->_M_allocate_node();//申请空的头结点
__try
{
this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node
- ); //改变start,令其指向新节点
this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - ; ////改变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 - ));
__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() / )//若果插入点在整体内存的前半段,插入后整体内存前移
{
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 + );//跳至下一个节点
_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 - );//跳至前一个节点
_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 >= && __offset < difference_type(_S_buffer_size()))//查找位置是否小于一个buffer的容量(在当前buffer内)
_M_cur += __n;
else
{
const difference_type __node_offset =
__offset > ? __offset / difference_type(_S_buffer_size())
: -difference_type((-__offset - )
/ _S_buffer_size()) - ;
_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标准库>>

STL标准库-容器-deque的更多相关文章

  1. STL标准库-容器-deque 双端队列

    头文件: #include<deque> 常用操作: https://www.cnblogs.com/LearningTheLoad/p/7450948.html

  2. STL标准库-容器-set与map

    STL标准库-容器-set与multiset C++的set https://www.cnblogs.com/LearningTheLoad/p/7456024.html STL标准库-容器-map和 ...

  3. STL标准库-容器-set与multiset

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key se ...

  4. STL标准库-容器-vector

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. 向量容器vector是一个动态数组,内存连续,它是动态分配内存,且每次扩张的原来的二倍. 他的结构如下 一 定义 vector ...

  5. STL标准库-容器-list

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. list 表示非连续的内存区域,并通过一对指向首尾元素的指针双向链接起来,从而允许向前和向后两个方向进行遍历.在list 的任 ...

  6. STL标准库-容器适配器

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 上一节介绍了仿函数适配器,这节主要介绍容器适配器和迭代器适配器的概念,其实容器适配器和迭代器其适配器就是封装了一些其他class ...

  7. STL标准库-容器-rb_tree

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 红黑树,关联式容器底层实现(map set),在使用中基本运用不到,但是还是想了解一下他的运作方式 Red_Black tree ...

  8. STL标准库-容器-unordered_set

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 unordered_set与与unordered_map相似,这次主要介绍unordered_set unordered_set ...

  9. STL标准库-容器-map和multimap

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 map与multimap为关联容器,结构如下 map底层实现依然是rb_tree 他的data可以改,但是key不能改,因此ma ...

随机推荐

  1. Python isdigit() isalnum()

    Python isdigit() 方法检测字符串是否只由数字组成. 返回值 如果字符串只包含数字则返回 True 否则返回 False. >>> choice = input(&qu ...

  2. 用SQL语句检查CPU和磁盘空间

    --查看4小时内的CPU变化值,1分钟统计一次 DECLARE @ts_now BIGINT; SELECT @ts_now = ms_ticks FROM sys.dm_os_sys_info; - ...

  3. AE读取CAD图层包括注记

    public override void FillDatabase(Teigha.DatabaseServices.Database pDb) { IFeatureClassContainer pFe ...

  4. MR的shuffle和Spark的shuffle之间的区别

    mr的shuffle mapShuffle 数据存到hdfs中是以块进行存储的,每一个块对应一个分片,maptask就是从分片中获取数据的 在某个节点上启动了map Task,map Task读取是通 ...

  5. POJ1144:Network(无向连通图求割点)

    题目:http://poj.org/problem?id=1144 求割点.判断一个点是否是割点有两种判断情况: 如果u为割点,当且仅当满足下面的1条 1.如果u为树根,那么u必须有多于1棵子树 2. ...

  6. PAT 1061 Dating[简单]

    1061 Dating(20 分) Sherlock Holmes received a note with some strange strings: Let's date! 3485djDkxh4 ...

  7. mysql性能测试-tpcc

    mysql性能测试-tpcc Tpcc-mysql TPC-C是专门针对联机交易处理系统(OLTP系统)的规范 Tpcc-mysql由percona根据规范实现 TPCC流程  更能模拟线上业务   ...

  8. 数据挖掘-KNN-K最近邻算法

    1. 算法核心思想: 通过计算每个训练样本到待分类样本的距离,选取和待分类样本的距离最近的 K 个训练样本,K个样本中那个类别的训练样本占据着多数, 则表明待分类的样本就属于哪一个类别. KNN算法在 ...

  9. The same month as the adidas NMD Singapore is releasing

    Earlier this December 2017, the inaugural adidas NMD Singapore silhouette released in the first colo ...

  10. centos6.5/6.6配置java环境以及数据库

    配置java环境 一.解压jdk 二.配置环境变量 1.修改修改/etc/profile文件(推荐开发环境使用,因为所有用户shell都有权使用这些环境变量,可能带来环境问题) 在profile末尾加 ...