STL deque详解

摘要:
例如,在什么情况下可以使用deque而不是vector来获得更好的结果。在内存分配中,解释vector和deque之间的区别。我们强烈建议您读完这篇关于如何使用STL容器vectorDequeOverviewdeque的文章。假设读者已经知道如何有效地使用向量容器。我在下面列出了一个deque成员函数表。

英文原文:http://www.codeproject.com/Articles/5425/An-In-Depth-Study-of-the-STL-Deque-Container

绪言
这篇文章深入的角度认 识 STL deque 容器。这篇文章将讨论一些有关deque的情况,比如在何种情况下你可以用deque代替vector以取 得更好的效果。读完这篇文章后,你应该能从容器膨胀,性能,内存分配方面解释 vector 与 deque 的不同。我们强烈推荐您读完这篇文章 关于 怎样使用STL 容器vector
Deque 概述
deque,与 vector都是 Standard Template Library 的一部分。 deque或者称之为 双向队列容器, 表面上与 vector 非常相似。甚至能在许多实现中直接替换 vector。 我们假设读者已经知道了怎样有效的使用vector容器 我已经在下面列出一张deque成员函数的表格。方便于参考与比较。
Deque Member Functions1
Function
Description
assign
Erases elements from a deque and copies a new set of elements to the targetdeque.
at
Returns a reference to the element at a specified location in the deque.
back
Returns a reference to the last element of the deque.
begin
Returns an iterator addressing the first element in the deque.
clear
Erases all the elements of a deque.
deque
Constructs a deque of a specific size or with elements of a specific value or with a specific allocator or as a copy of all or part of some other deque.
empty
Tests if a deque is empty.
end
Returns an iterator that addresses the location succeeding the last element in adeque.
erase
Removes an element or a range of elements in a deque from specified positions.
front
Returns a reference to the first element in a deque.
get_allocator
Returns a copy of the allocator object used to construct the deque.
insert
Inserts an element or a number of elements or a range of elements into thedeque at a specified position.
max_size
Returns the maximum length of the deque.
pop_back
Deletes the element at the end of the deque.
pop_front
Deletes the element at the beginning of the deque.
push_back
Adds an element to the end of the deque.
push_front
Adds an element to the beginning of the deque.
rbegin
Returns an iterator to the first element in a reversed deque.
rend
Returns an iterator that points just beyond the last element in a reverseddeque.
resize
Specifies a new size for a deque.
size
Returns the number of elements in the deque.
swap
Exchanges the elements of two deques.
Deque Operators1
Function
Description
operator[]
Returns a reference to the deque element at a specified position.
对于与vector如此惊人的相似,我们提出了如下问题:
Q: deuquevector有如此相似的函数,那哪个更优越内?
A: 如果你非要我回答的话.....vector.
好,能稍微解释一下吗?
很乐意回答你的问题,我当然不是凭空捏造的,这个答案来自于 C++ Standard2 itself. Section 23.1.1 中的如下声明:
vector 是一个你因该默认选择的容器. ... 而 deque仅仅优先选择于大量的首或尾删除操作.
非常有趣,这篇文章的目的就是为了完全阐明上句话的含义. 
What's New
我们刚刚比较了deque与vector两者的成员函数,发现deque较vector多了如下两个函数.
  1. push_front() - Adds elements to the front of a deque.
  2. pop_front() - Removes elements from the front of a deque.
他们从语法结构上来看与 push_back() and pop_back()一样。 这里产生了第一条使用 deque的理由,曰:你需要从首或尾双向追加元素。
What's Missing
我们又可以发现下面两个元素是vector特有的
  1. capacity() - Returns the current capacity of a vector.
  2. reserve() - Allocates room for a specified number of elements in a vector.
这里真正的翻开了本次学习的第一页. 他告诉了我们 vector与 deque内部数据管理的根本不同。 deque分配内存是一块块的,每当它插入固定数量的数据. 然而vector分配就近原则 (这并不是什么坏事). 但有趣的是当 vector意识到当前的内部缓冲不够在大时,就加大每个allocation,以满足当前的需要。 在后面实验当中,我们能很好的证明 deque为什么不需要capacity()和 reserve() .
实验 1 - 容器膨胀
目的
这个实验用来观测vector与 deque膨胀时的区别. 实验结果以插图形式从物理内存分配与程序性能两分面说明。
描述
这个测试用小程序从文件中读入数据,以行为单位push_back()到 vector与 deque中. 目的是产成大量的插入动作,文件可能读不止一次。本次的小程序如下:
#include <deque>
#include <fstream>
#include <string>
#include <vector>
 
static enum modes
{
    FM_INVALID = 0,
    FM_VECTOR, 
    FM_DEQUE   
};   
 
class CVectorDequeTest 
{   
 public:
      CVectorDequeTest();   
     
      void ReadTestFile(const char* szFile, int iMode)   
      {       
          char buff[0xFFFF] = {0};
          std::ifstream    inFile;
          inFile.open(szFile);
         
          while(!inFile.eof())
          {
              inFile.getline(buff, sizeof(buff));
             
              if(iMode == FM_VECTOR)
                      m_vData.push_back(buff);
              else if(iMode == FM_DEQUE)
                      m_dData.push_back(buff);
          }       
         
          inFile.close();
         
       }   
      
       virtual ~CVectorDequeTest();
 
 protected:   
      std::vector<std::string> m_vData;   
      std::deque<std::string> m_dData;
 };
结果
本次测试的物理环境:
Processor
1.8 GHz Pentium 4
Memory
1.50 GB
OS
W2K-SP4
No. of Lines in File
9874
Avg. Chars per Line
1755.85
No. of Times File Read
45
Total Elements Inserted
444330
系统的性能由windows自带的Task Manager表明,程序的耗时由 Laurent Guinnard's CDuration class 实现. 实验结果如下:
我们注意到,在vector重新分配时会有一个峰,为何峰值越来越大在vector分配内部缓冲存储。而deque却没有这种行为,随着数据插入成线性增长。 当 deque释 放时,内存回收成曲线下降,是我们最初没有预料到的。我们起先预测内存释放因该看到去与vector很相似。看来我们南非要更多的一些测试。我们能够建立 一些假设: deque内存并不是邻近的,那么回收起来会更加复杂。我们稍后验证这个假设,先来分析一下本次实验的性能分析结果。
内存分配花了多长时间?
显而易见,当vector在扩容时没有任何新的元素插入。
每个容器的push_back()花费了多长时间也是比较有趣的. 如下所示。记住,每个例子追加了9874个元素,平均长度1755.85。
测试 2 - vector::reserve()效果
目的
本次实验的目的是观察在大量元素追加前调用vector的reserve()函数的变化情况, 从内存分配与性能上比较与deque区别,
描述
本次实验和一基本相同,只不过多了下面一句话。
m_vData.reserve(1000000);
结果
测试环境地s:
Processor
1.8 GHz Pentium 4
Memory
1.50 GB
OS
W2K-SP4
No. of Lines in File
9874
Avg. Chars per Line
1755.85
No. of Times File Read
70
Total Elements Inserted
691180
系统的性能由windows自带的Task Manager表明,程序的耗时由 Laurent Guinnard's CDuration class 实现. 实验结果如下:
很有趣!vector不再需要分配更多的内部缓冲存储。reserve()使得vector有足够的空间一次性容下我们所有的测试数据,691180个!.对与deque的释放猜想, 观察到内存释放时间较上一次实验大幅度增长.在我们下一个实验来搞定这个问题.
内存分配性能有多大提升?
下面张图描述了元素追加所花的时间:
正如你所看到了,vector和deque在在追加元素的性能性能上不分伯仲,然而, vector在插入给定的元素的时间上有一些轻微的离散参考的图:
总体变化 vector vs. deque以他花费入9874 个 平均度有 1755.85 长的元素,如下表所示:
Vector
Deque
Mean
0.603724814 sec
Maximum
0.738313000 sec
Minimum
0.559959000 sec
Std. Dev
0.037795736 sec
6-Sigma
0.226774416 sec
Mean
0.588021114 sec
Maximum
0.615617000 sec
Minimum
0.567503000 sec
Std. Dev
0.009907800 sec
6-Sigma
0.059446800 sec
实验 3 - 内存回收
目的
本次实验的目的是分析内存和试着证实我们的猜测:deque内存的因为非邻近的关系而释放内存有点困难。
描述
这个测试类来自实验1, 调用函数专为分配测试类的增长大小而设计,并记入数据。,具体实现好下:
    for(xRun=0; xRun<NUMBER_OF_XRUNS; xRun++)
    {
        df = new CVectorDequeTest;
 
        elapsed_time = 0;
        for(i=0; i<NUMBER_OF_RUNS*xRun; i++)
        {
            cout << "Deque - Run " << i << " of " << NUMBER_OF_RUNS*xRun << "... ";
            df->ReadTestFile("F:\huge.csv",DF_DEQUE);
 
            deque_data.push_back(datapoint());
 
            deque_data.back().time_to_read = df->GetProcessTime();
            elapsed_time += deque_data.back().time_to_read;
 
            deque_data.back().elapsed_time = elapsed_time;
 
            cout << deque_data.back().time_to_read << " seconds ";
        }
 
        vnElements.push_back(df->GetDequeSize());
 
        cout << " Deleting... ";
 
        del_deque.Start();
        delete df;
        del_deque.Stop();
 
        cout << del_deque.GetDuration()/1000000.0 << " seconds. ";
 
        vTimeToDelete.push_back(del_deque.GetDuration()/1000000.0);
    }
结果
主次实验的平台与上两次的一样。除了内存分配的数量不同从,用70次增长从9874 到 691180。下图指出deque内存回收时间依deque里的元素个数deque由平均长度为1755.85字节的字符串填冲。
尽管用几个真实的时间数据和拟合线有较大出入。但总体来说拟合线从使精确度控制在 R2=95.15%. 下表给出的数据都背离了拟合线。
deque Results
Mean
0.007089269 sec
Maximum
11.02838496 sec
Minimum
-15.25901667 sec
Std. Dev
3.3803636 sec
6-Sigma
20.2821816 sec
用同样的放式测能vector,见下图:
这次数据的精确度 R2=81.12%. 这可以通过更多的测试进一步题高.但是, 数据已经能很好的说明了结果, 下表结出的数据都和拟合线有较大出入。
vector Results
Mean
-0.007122715 sec
Maximum
 0.283452127 sec
Minimum
-0.26724459 sec
Std. Dev
0.144572356 sec
6-Sigma
0.867434136 sec
实验 4 - vector::insert() vs. deque::insert()的性能特性。
目的
deque以保证等时间插入insert()出了名 他能与vector::insert()对抗吗? 本次实验的目的是 (不必惊讶)观查vector::insert()与 deque::insert()之间的性能特性。
描述
也许有几次发现从容后面插入并不能达到你的目标,这种情况下,你多半会祭出insert()来解决当前困难。本次的实验程序和实验1的一样,仅仅是把push_back由insert()代替。
结果
从下面的图中看到,比起vector来 deque的常量时间差能力令人瞠目皆舌,强!
时间轴差别醒目, (61810元素).
实验 5 - 容器读取性能
目的
这个实验比较vector::at()vector::operator[]deque::at()与 deque::operator[]之间的性能差. 想得到operator[]快于at(),因为operator[]没有边界检测能力。自然,还有vector与deque的私人恩怨.
描述
本次测试将插入1000000长度为1024字符的字串到每个容器。并且通过at()和opearator[]读出。测试50次,结果以统计表形式给出。
解果
也许有点惊讶,vector和deque访问元素能力之间的区别非常小。还有几乎可以忽略的at与operator[]之间的差别,结果如下。
vector::at()
Mean
1.177088125 sec
Maximum
1.189580000 sec
Minimum
1.168340000 sec
Std. Dev
0.006495193 sec
6-Sigma
0.038971158 sec
deque::at()
Mean
1.182364375 sec
Maximum
1.226860000 sec
Minimum
1.161270000 sec
Std. Dev
0.016362148 sec
6-Sigma
0.098172888 sec
vector::operator[]
Mean
1.164221042 sec
Maximum
1.192550000 sec
Minimum
1.155690000 sec
Std. Dev
0.007698520 sec
6-Sigma
0.046191120 sec
deque::operator[]
Mean
1.181507292 sec
Maximum
1.218540000 sec
Minimum
1.162710000 sec
Std. Dev
0.010275712 sec
6-Sigma
0.061654272 sec
结论
这种篇文章中,我们覆盖了几个不同的情况,关于vector和deque的取舍,请参考以下几点。
当有大量数据要push_back时,记得要vector::reserve().
在实验1中,我们研究了vector和deque的增长行为,在这次场景中,我们看到因为deque的特性而有一个固定的增长率。vector使我们考虑使用reserve(),实验2很好的表明了这一切。
如果有大量释放操作,比起vectordeque将花费更多的时间.
In Experiment 3, we explored the differences between reclaiming the contiguous and non-contiguous memory blocks ofvector and deque, respectively. The results proved that vector reclaims memory in linear proportion to the number of elements whereas deque is exponential. Also, vector is several orders of magnitude than deque in reclaiming memory. As a side note, if you are performing your calls to push_back() within a tight loop or sequence, there is a significant possibility that most of the memory deque obtains, will be contiguous. I have tested this situation for fun and have found the deallocation time to be close to vector in these cases.
如果你打算用insert或者有pop_front()的需要,使用deque.
Well, ok, vector doesn't have pop_front(), but based on the results of Experiment 4, it might as well not have insert()either. The results of Experiment 4 speak volumes about the need for deque and why it is part of the STL as a separate container class.
对于元素访问vector::at()明显胜了,(JiangMiao:不是因为他快,而是他的的不慢且安全).
After summing up the statistics of Experiment 5, I would have to say that although all the methods were close,vector::at() is the winner. This is because of the best balance between the raw mean of the access times as well as the lowest 6-sigma value.
What's all this 6-Sigma stuff?
Although a popular buzzword in industry today, 6-Sigma 在 统计学中有着举足轻重的地位. 如果你要生成高斯分部(正态分部) 为你的样本程序, 你能看到一些数据误差很大 (the symbol for std. deviation is the Greek letter, sigma, BTW) from the mean, you will have 68.27% of the area under the curve covered. At 2 标准偏差, 2-Sigma, you have 95.45% of the area under the curve, at 3 标准偏差, you will have 99.73% of the area and so forth until you get to 6 标准偏差, when you have 99.99985% of the area (1.5 Defects per million, 6-Sigma).
Final Words
I hope you have gained some insight into deque and have found this article both interesting and enlightening. Any questions or comments are certainly welcome and any discussion on vector or deque is encouraged.
References
  1. Plauger, P.J. Standard C++ Library Reference. February, 2003. MSDN.
  2. ISO/IEC 14882:1998(E). Programming Languages - C++. ISO and ANSI C++ Standard.
  3. Schildt, Herbert. C++ from the Ground Up, Second Edition. Berkeley: 1998.
Sutter, Herb. More Exceptional C++. Indianapolis: 2002.
 
deque从逻辑上来看是连续的内存,本质上是由一段段固定大小 的连续空间组成。deque采用类似索引的结构管理内存,如下: 
STL deque详解第1张
 

采用一小块连续的内存索引缓存结点,每一个缓存结点也是一段连续的空间,可以存储多个数据。当索引内存空间满载,需要申请一块更大的内存做索引。 

4. 为什么deque没有capacity和reserve 

vector有capacity和reserve函数,deque和list一样,没有capacity和reserve函数。之所以这样,主要是因为list和deque两者都没有必要保留这两个函数,list是以1为增量动态增加内存,deque则是分段动态增加内存 。capacity返回当前所分配的内存块大小,vector在调用函数reserve(n)之后,其capacity将至少为N=n,reserve函数的作用是当现有内存空间小于N时重新分配内存。如果事先知道要插入N个元素,则与不断分配内存相比,预先调用reserve可以加快程序的运行速度。 

底层:

http://blog.csdn.net/mdl13412/article/details/6647409

免责声明:文章转载自《STL deque详解》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇kolla-ansible-----rally模块VS2010 报表教程--玩转机房重构下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

CSS3常见动画

一、是什么 CSS动画(CSS Animations)是为层叠样式表建议的允许可扩展标记语言(XML)元素使用CSS的动画的模块 即指元素从一种样式逐渐过渡为另一种样式的过程 常见的动画效果有很多,如平移、旋转、缩放等等,复杂动画则是多个简单动画的组合 css实现动画的方式,有如下几种: transition 实现渐变动画 transform 转变动画...

Java实现 蓝桥杯 历届试题 最大子阵

问题描述   给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。 其中,A的子矩阵指在A中行和列均连续的一块。 输入格式   输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。   接下来n行,每行m个整数,表示矩阵A。 输出格式   输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。 样例输入 3 3 -1...

计算几何 + 欧拉定理 (一笔画)

题目大意:依次给定多个点(要求第一个点和最后一个点重叠),把前后两个点相连求最后得到的图形的面的个数 思路分析 : 借助欧拉定理,V+F-E = 2,只要求出点的数量和边的数量就可以计算出面的数量,点的数量只要枚举直线就可以,但是有可能有重复的点,之最去重一下就可以,然后在枚举剩下的点出现在几条直线中。 代码示例:(未测试) #define ll long...

基于Selenium2+Java的UI自动化(7)- 模拟键盘鼠标操作

webdriver提供Actions类,来模拟鼠标点击、悬浮、拖拽、键盘输入等操作; 一、鼠标双击、右击 selenium模拟鼠标单击是用WebElement.click(); 方法,但是双击、右击,需要使用Actions类来模拟; package com.automation.actions; import org.openqa.selenium...

《深入浅出WPF》笔记——命令篇

 一、认识命令 1.1命令的特点   提到“命令”,我们应该想到命令的发出者,命令的接受者,命令的内容,准备工作,完成任务,回报工作。。。与事件中的发送者,接受者,消息,处理,处理,处理一一对应,如果是单纯的几个对应关系,的确用事件是能够代替的,不过,命令相对事件有其自己的特点的。比如,古时候,如果两个部落发动战争,经常在出军之前,做了充分的准备,才可能一...

C#中的WebBrowser控件的使用

作者:txw1958 原文:http://www.cnblogs.com/txw1958/archive/2012/09/24/CSharp-WebBrowser.html 0、常用方法 Navigate(string urlString):浏览urlString表示的网址 Navigate(System.Uri url):浏览url表示的网址 Nav...