原文链接:http://www.cnblogs.com/ourroad/p/3356053.html
STL中deque是我们常说的双端队列,既可以从头添加元素,也可以从尾部添加元素,deque的成员函数和vector的成员函数十分相似,但是它们的内部实现却又很多不同.
deque的模板声明:
template < class T, class Allocator = allocator< T> > class deque;
1)deque的内存分配方式,deque的内存管理方式比vector复杂.
![](http://images.cnitblog.com/blog/339657/201310/07210947-5917cb02d189433d83461032d3c8b01f.png)
上面程序在执行完最后的push_back后内存分布如下图:
![](http://images.cnitblog.com/blog/339657/201310/07211001-f407f68e8bff4ceab4297e180a38d23c.png)
追踪push_back函数我们看到:
![](http://images.cnitblog.com/blog/339657/201310/07211014-b226cfe294754e33b70dee7be0676713.png)
我们先看几个变量的含义, _Myoff成员变量始终表示队头距离存储空间开始位置的元素个数, _DEQUESIZ宏定义如下,可以看到为了避免太小内存块,_DEQUESIZ会根据我们定义类型大小来确定一个block大小.
_Mysize表示队列中实际元素个数,_Map是一个_Ty**类型二级指针,正是挂接block的chunk, 宏_DEQUEMAPSIZ正是_Map的最小分配粒度.
![](http://images.cnitblog.com/blog/339657/201310/07211027-1652b5736c9d4b2992221cb1021e57f6.png)
如果存储空间不足时_DEQUESIZ我们要进行内存分配, 可以看到程序根据_Myoff和_Mysize来计算block编号,因此我们可以把_Map看成是一个环,而所有的block也可以看成一个环.在前面计算好block后,
判断该block是否已经分配内存,如果没有则分配内存,如果有则直接对新添加的对象在分配的内存中进行构造.
下面我们看下_Growmap函数,这里我做了简化,把copy数据的过程去掉了,我们可以看到在我们重新分配内存大小是以原来chunk大小的一半进行增加,如果增加大小小于最小粒度_DEQUEMAPSIZ,则取最小粒度,
如果用户需求大于我们计算的值,则以用户需求值进行增加,然后释放原来_Map的空间.
![](http://images.cnitblog.com/blog/339657/201310/07211047-1b4ef895f7bb4ecdbbb6c4a9c8013b57.png)
其他push_front操作和push_back操作类似,只是在计算block位置是不一样, 如下:
![](http://images.cnitblog.com/blog/339657/201310/07211106-312c064ac9bd4ea59d3f267919adce6f.png)
2)deque其他操作,pop_front和pop_back时只是将队头和队尾元素移除,
进行insert操作时有点复杂,我们看看插入在一个位置插入多个元素的情况.
![](http://images.cnitblog.com/blog/339657/201310/07211124-b0c9b28fbd4848c994b6320828555ae8.png)
上面列出了靠近队头位置时的处理情况,靠近队尾时情况差不多,这里不再贴出,insert的其他重载函数操作类似.
deque在进行erase进行移除元素时,要比insert操作简单多了,如下:
![](http://images.cnitblog.com/blog/339657/201310/07211138-f5034892752d4072b1e0190a39000e5c.png)
deque的clear操作和vector不一样,这里clear的话是直接将内存释放掉了.
总结:deque在进行内存管理上更复杂,但与vector比较内存管理更加有效,特别是对大量数据序列,不过我们可以看到内存在根据我们的需求不断申请,但是没有释放,如果在数据量波动比较大的地方,可能比较消耗内存,
不过我们可以使用clear函数将内存释放掉,我想clear这么做肯定是考虑到了这个需求.