1.deque简介:
deque容器同样是一种顺序容器,你可以了解你的元素的存储位置,你可以安排你的元素的存储位置。和vector相比,deque可以实现用常数的时间在容器头部插入元素。同样deque也没有容量的概念,这是因为deque可以动态地增加连续空间,而不需要像vector那样,因为之前的空间不够用,必须重新找一块内存,把数据拷贝过去,然后销毁之前的。
虽然deque也提供了随机迭代器,但其迭代器并不是像vector中是一个普通的指针,其实是实现很复杂,所以,除非你的需求特别适合deque,可以优先考虑vector(当然我们在这只说他们两个,是因为它们都有随机存取能力)
ps:(由于个人能力问题,我们只是从大体的角度去看这个容器,达到在工作中能够随心所欲地使用就可以了,具体的实现代码,就不去挖掘,明白实现思路就好了)
2.deque的实现
2.1 deque内存管理的基本思路:
deque源码(删减的)
template <class T>
class deque {
protected:
iteraotr start;//表示第一个节点 iterator是一个迭代器,我们到时候再看这个类
iteraotr finish;// 表现最后一个节点
protected:
T** map; // 指向map, map是一个连续的空间, 其每个元素都是一个指向缓冲区的指针
size_type map_size; // map容量
}
这样其实就很明白了,在deque的类中,我们有一个指向指针数组的指针,根据这个指针我们可以找到,指针数组所在的地址,这个数组其实叫中控器。中控器中的每个元素,分别指向一个固定大小的连续空间,这段空间叫做缓冲区,我们的数据其实就是存储在缓冲区中的。当我们的数据多到盛不下时,就可以重新分配一些连续的内存扩大容量。
2.2 deque的迭代器
可以看出来,deque要想支持随机访问,它的跌代器就要设计的很复杂,首先要能够找到数据存储的地址,为了方便找到下一个数据的位置,如果在同一个缓冲区内它必须有两个指向缓冲区头和尾的指针。如果下一个位置在另外的缓冲区内,则必须知道中控器的地址。其实,人家的实现只是一种思路,你可以疑惑为什么迭代器还要用两个first和last指针呢,如果 没有它们,我是不是同样的可以了解到当前指针的位置,是不是已经到了末尾?假设没有它们,我们要想知道当前的位置,就必须用当前位置的指针,与中控器元素的指针做减法与缓冲区大小作比较,而且有时,我们还可能从缓冲区的末尾开始增加元素,很费劲的。所以,多管理一些指针会很方便。
2.3 存有元素的deque内存状态
deque类中还有两个start 和finish迭代器可以看出,在存入元素时,开始的时候是从中控器的最中间开始存的,start和finish分别指向第一个元素和最后一个元素的下一个位置。
了解的也就这些吧,思路很好,要是写一个这样的容器,现在肯定还没有这样的水平。所以改日再看看代码吧。
3. deque的一些成员函数
c.assign(beg,end) 将[beg; end)区间中的数据赋值给c。
c.assign(n,elem) 将n个elem的拷贝赋值给c。
c. at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
c.back() 传回最后一个数据,不检查这个数据是否存在。
c.begin() 传回迭代器中的第一个数据。
c.clear() 移除容器中所有数据。
c.empty() 判断容器是否为空。
c.end() 指向迭代器中的最后一个数据地址。
c.erase(pos) 删除pos位置的数据,传回下一个数据的位置。
c.erase(beg,end) 删除[beg,end)区间的数据,传回下一个数据的位置。
c.front() 传回第一个数据
c.insert(pos,elem) 在pos位置插入一个elem拷贝,传回新数据位置c.insert(pos,n,elem) 在pos位置插入>n个elem数据。无返回值c.insert(pos,beg,end) 在pos位置插入在[beg,end)区间的数据。无返回值c.max_size() 返回容器中最大数据的数量。
c.pop_back() 删除最后一个数据
c.pop_front() 删除头部数据。
c.push_back(elem) 在尾部加入一个数据。
c.push_front(elem) 在头部插入一个数据。
c.rbegin() 传回一个逆向队列的第一个数据。
c.rend() 传回一个逆向队列的最后一个数据的下一个位置。
c.resize(num) 重新指定队列的长度。
c.size() 返回容器中实际数据的个数。
4.list的简介
stl中的list其实是一个双向循环列表,既然是链表,就有链表的特点。不支持随机访问,但是插入和删除操作效率高。主要是它还有一些对链表的操作,可以很好的锻炼我们的编程能力。当然,有机会专门要写一篇这种东西。
5.list的内存结构
在没有数据时,list只有个空的节点,这个结点两个指针指向自己。
6.list的一些成员函数
assign()给list赋值
back()返回最后一个元素
begin()返回指向第一个元素的迭代器
clear()删除所有元素
empty()如果list是空的则返回true
end()返回末尾的迭代器
erase()删除一个元素
front()返回第一个元素
insert()插入一个元素到list中
max_size()返回list能容纳的最大元素数量
merge() 合并两个list
pop_back()删除最后一个元素
pop_front()删除第一个元素
push_back()在list的末尾添加一个元素
push_front()在list的头部添加一个元素
rbegin()返回指向第一个元素的逆向迭代器
remove()从list删除元素
remove_if()按指定条件删除元素
rend() 指向list末尾的逆向迭代器
resize()改变list的大小
reverse()把list的元素倒转
size()返回list中的元素个数
sort()给list排序
splice()合并两个list
swap()交换两个list
unique()删除list中重复的元素
7.vector、list、deque、三个容器的选择:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
参考资料:《STL源码剖析》