deque 是一种双端队列,是一种能动态调整大小的序列容器,可以在两端(前面或后面)扩展或收缩。不同的标准库实现不一样,但是通常是用动态数组来实现,一般都允许通过随机访问迭代器直接访问某个元素,并根据需要自动扩展和收缩容内存空间。这个特点和vector很像,但是vector不能自动收缩,而deque可以。deque的另一个特点是即使在容器的开头插入元素也能保证高效。但是,与vector不同,deque不能保证将其所有元素的存储空间都是连续的,这意味着通过指针+偏移的方式来访问元素可能比较危险,可能会越界。deque的内部结构可以看做是内存分组连续,就是同一个组内的元素是连续存储的,而不同的组可能不是连续的。deque最大的任务就是在这些分组定量连续空间上,维护其连续的假象。
deque和vector都提供了随机访问接口(operator[]),但在内部它们的工作方式是完全不同的。deque是由一段一段的相同大小的连续内存空间构成,并由一个中控器来管理这些内存分段,让这些分散的内存块从逻辑上看起来像是连续的,但连续只是一个假象。当进行随机访问时,中控器首先要计算访问的元素在哪个分组,然后再通过计算元素在分组中的偏移量来访问元素。这里已经可以看到deque的随机访问要比vector复杂。同样的道理,deque的迭代器的自加(++)自减(–)和加减运算也会比vector要复杂。deque迭代器自加自减操作,可能要从一个分组调到另一个分组;而加减操作还可能会跳跃几个分组。
由于deque这种分组连续的结构,带来的一个好处是扩容更加方便快捷。vector扩容要分三步走,申请新的内存空间,复制元素,销毁旧元素;而deque扩容只需要新建一个分组然后把它插入到头尾就可以了。在元素数量比较大的时候,vector的“重新配置,复制,释放”操作会消耗大量的CPU时间,deque这个优势就更加明显了。由于这个原因,deque已经没有必要提供reserve内存空间预留接口。deque这种结构的另一个好处是,当需要收缩空间的时候,直接把空闲的分组释放掉就可以,而vector并不能自动收缩。