欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STL之deque(一)

程序员文章站 2024-02-12 22:20:22
...

       概述:

       vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。所谓双向开口,意思是可以在头尾两端分别做元素的安插和删除动作。vector当然也可以在头尾两端做动作,但是其头部动作效率奇差,无法被接受。

       deque和vector的最大差异,一在于deque允许在常数时间内对头尾端进行元素的安插或移除动作。二在于deque没有所有容量概念,因为他是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。换句话说,像vector那样因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间这样的事情在deque是不会发生的。因此,deque没有必要提供所谓的空间保留功能。

       虽然deque也提供Ramdon Access Iterator,但它的迭代器并不是原生指标,其复杂度和vector不可以道里计,这当然在影响各个运算层面。因此,除非必要,我们应尽量选择使用vector而非deque。对deque进行的排序动作,为了最高效率,可将deque先完整复制到一个vector身上,将vector排序好,再复制回deque。

       中控器:

       deque是连续空间,连续线性空间总令我们联想到array或vector。array无法成长,vector虽可成长,却只能向尾端成长,而且其所谓成长原是个假象,事实上是(1)另寻找更大空间,(2)将原数据复制过去,(3)释放原来空间三部曲。如果不是vector每次配置新空间都留下一些富裕,其成长假象所带来的代价是相当高贵。

       deque是由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的界面。避开了“重新配置,复制,释放”的轮回,代价则是复杂的迭代器架构。

       受到分段连续线性空间的字面影响,我们可能以为deque的操作复杂度和vector相差不多。其实不是这样,虽然是分段连续线性空间,就必须有*控制,而为了维护整体连续的假象,数据结构的设计及迭代器前进后退等动作颇为繁琐。deque的操作代码量远比vector或list都多得多。

       deque采用一块所谓的map,(不是STL的map容器)作为主控。这里所谓map是一小段连续空间,其中每个元素(此处称为一个节点,node)都是指标,指向另一段较大的连续线性空间,称为缓冲区。缓冲区才是deque的存储空间主体。SGI STL允许我们制定缓冲区大小,默认值0表示将使用512bytes缓存区。      

template <class T, class Alloc = alloc, size_t BUFSIZ = 0>
class deque {
public:              //基本类型
	typedef T value_type;
	typedef value_type* pointer;
    ...
protected:          //内部类型
	typedef pointer* map_pointer;
protected:
	map_pointer map; //指向map,map是一个T**,也就是一个指针,其所指之物又是指针,
                         //指向型别为T的一块空间。map是块连续空间,其内的每个元素
	                 //都是一个指针(称为节点),指向一个缓冲区
	size_type map_size; //map内可以容纳多少指针
};

       map其实是一个T**,也就是说他是一个指标,所指之物又是一个指标,指向类型为T的一块空间。

       STL之deque(一)

       deque迭代器:

       deque是分段连续空间。维护其整体连续假象的任务,着落在迭代器的operator++和operator--两个运算子上。

       deque迭代器应该具备什么结构。首先,他必须能够指出分段连续空间(亦即缓冲区)在哪里,其次他必须能够判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退时必须跳跃至下一个或上一个缓存区。为了能够正确跳跃,deque必须随时掌握管控中心(map)。       

// __deque_iterator的数据结构
template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator
{
	typedef __deque_iterator<T, T&, T*>             iterator;
	typedef __deque_iterator<T, const T&, const T*> const_iterator;
	static size_t buffer_size() {return __deque_buf_size(0, sizeof(T)); }

	typedef random_access_iterator_tag iterator_category;
	typedef T value_type;
	typedef Ptr pointer;
	typedef Ref reference;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	typedef T** map_pointer;

	typedef __deque_iterator self;

	// 保持与容器的联结
	T* cur;       // 此迭代器所指之缓冲区中的现行元素
	T* first;     // 此迭代器所指之缓冲区的头
	T* last;      // 此迭代器所指之缓冲区的尾(含备用空间)
	map_pointer node;    // 指向管控中心
        ...
}

      其中用来决定缓冲区大小的函数buffer_size(), 呼叫__deque_buf_size():

inline size_t __deque_buf_size(size_t n, size_t sz)  
{  
  return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));  
}

       如果n不为0,传回n,表示buffer size由使用者自定。

      如果n为0,表示buffer size使用默认值,那么,如果sz(元素大小,sizeof(value_type))小于512,传回512/sz,如果sz不小于512,传回1。下图显示deque的中控器,缓冲区,迭代器的相互关系。

      STL之deque(一)

       STL之deque(一)

    deque::begin()传回迭代器start,deque::end()传回迭代器finish。这两个迭代器都是deque的data members。