Deque的基本概念
容器deque和vector非常相似,内部也实现为一个dynamic array来管理元素,提供随机访问,并有着和vector几乎一样的接口,不同的是deque的dynamic array头尾都开放,因此在首尾都能进行快速安插和删除
deque通常实现为一组独立区块,第一区块朝某方向扩展,最末区块朝另一方向扩展
使用deque之前,必须包含头文件<deque>
#include<deque>
deque类型定义于命名空间std内的一个class template
namespace std{
template<typename T,typename Allocator=allocator<T>>
class deque;
}
注:和vector相同,这里第一个template参数表示元素的类型,第二个template实参可有可无,用来指定内存模型,默认为allocator
Deque的能力
与vector相比,功能相比的差别如下
deque在首位删除和添加元素都可以在常量时间完成
访问元素时deque内部会有一个间接的过程,所以元素的访问和迭代器的动作都会稍慢一些
迭代器需要在不同的区块内跳转,因此必须是个smart pointer ,而不是一个寻常pointer
deque不支持对容量和内存重新分配时机的控制。不过,deque的内存分配优于vector,因为其内部结构显示,deque不必在内存重新分配时,复制所有的元素
deque会释放不使用的内存区块
以下功能和vector基本相同
在中段安插,移除元素速度相对较慢(保持相对位置的原因)
迭代器属于random-access iterator
总之在以下情况下最好采用deque
你需要在两端安插或移除元素时(这是deque的拿手好戏)
无需指向(refer to)容器内的元素
要求”不再使用的元素必须释放“(不过C++11standard对此无任何保证)
Deque的操作函数
deque<T>c f //default构造函数,产生一个空容器
deque<T>c(c2) //copy构造函数,将c作为c2的一份拷贝
deque<T>c=c2 //copy构造函数,将c作为c2的一份拷贝
deque<T>c(rv) //move构造函数,取rv的内容作为c的内容
deque<T>c=rv //move构造函数,取rv的内容作为c的内容
deque<T>c(n) //利用元素的default构造函数,初始化一个大小n的deque
deque<T>c(n,elem) // 建立一个大小为n,每个元素都是elem的deque
deque<T>c(begin,end)//建立一个以[begin,end)内的内容为初值的deque
deque<T>c(initlist)// 建立一个以initlist内的内容为初值的deque
deque<T>c=initlist// 建立一个以initlist内的内容为初值的deque
c.~deque()// 销毁所有的元素,释放内存
Deque的各项操作只有以下两点和vector不同
deque不提供容量操作
deque直接提供函数完成头部元素的安插和删除(push_front(),pop_front())
其他操作这里不重复
对于shrink_to_fit()操作函数,你可能认为没有什么意义,因为deque本就会释放用不到的区块内存。然而,deque内部用来存放”指向独立区块“的所有pointer的空间,通常不会被缩小,如果用了这个函数就真可能被缩小
Deque的非更易型操作
c.empty() //相当于size()==0,但也许较快
c.size() //返回当前容器的元素个数
c.max_size() //返回元素个数之最大可能量
c.shrink_ro_fit() //要求降低容量,以符合元素个数
c1==c2 // 返回c1是否等于c2
c1!=c2 // 返回c1是否不等于c2
c1>c2 // 返回c1是否大于c2
c1<c2 // 返回c1是否小于c2
c1>=c2 // 返回c1是否大于等于c2
c1<=c2 // 返回c1是否小于等于c2
c[idx] // 返回idx所指向的元素(不检查范围)
c.at(idx) // 返回idx所指向的元素(不检查范围)
c.front() // 返回第一元素
c.back() // 返回最末元素
c.begin() // 返回一个random-access iterator指向第一位元素
c.end() // 返回一个random-access iterator指向末尾下一位元素
c.cbegin() // 返回一个const random-access iterator指向第一位元素
c.cend() // 返回一个const random-access iterator指向末尾下一位元素
c.rbegin() // 返回一个reverse random-access iterator指向第一位元素
c.rend() // 返回一个reverse random-access iterator指向末尾下一位元素
c.crbegin()// 返回一个const reverse random-access iterator指向第一位元素
c.crend() // 返回一个cosst reverse random-access iterator指向末尾下一位元素
有一些特殊事项
处理at(),没有任何成员函数会”检查索引或迭代器是否有效“
元素的插入或删除可能导致内存重新分配,所以任何插入或删除动作都会使所以指向deque元素的pointer,reference和iterator失效。唯一例外的是在头部或尾部插入元素,在哪那动作之后pointer和reference仍然有效(但iterator就没有那么幸运)
Deque的更易型操作
c=c2 //将c2的全部元素赋值给c
c=rv //通过move assign的方式赋予c
c=initlist //将initlist的所有元素赋值给c
c.assign(n,elem)//复制n个elem赋给c
c.assign(beg,end)//将区间[begin,end)的元素赋给c
c.assign(initlist)//将initlist中的所有元素赋给c
c1.swap(c2)//置换c1和c2的数据
swap(c1,c2)//置换c1和c2的数据
c.push_back(elem)//附加elem的拷贝于容器的末尾
c.pop_back()//移除最后一个元素,但是不返回它
c.push_front()//附加elem的拷贝于容器的首处
c.pop_front()//移除第一个元素,但是不返回它
c.insert(pos,elem)//在pos的前方插入elem的一个拷贝,并且返回新元素的位置
c.insert(pos,n,elem)//在pos的前方插入elem的n个拷贝,并且返回第一个新元素的位置
c.insert(pos,beg,end)//在pos的前方插入elem的一个拷贝,并且返回第一个新元素的位置
c.insert(pos,initlist)//在pos的前方插入elem的一个拷贝,并且返回第一个新元素的位置
c.emplace(pos,args...)//在pos的前方插入以args作为初值的元素,并且返回新元素的位置
c.emplace_back(args...)//末尾插入以args作为初值的元素,并且返回新元素的位置
c.emplace_front(args...)//首处插入以args作为初值的元素,并且返回新元素的位置
c.erase(pos)//移除pos位置上的元素,并返回下一元素的位置
c.erase(beg,end)//移除[begin,end)区间内的元素,并返回下一元素的位置
c.resize(num)//将元素数量改为num,如果size()变大,则以元素default构造函数完成初始化
c.resize(num,elem)//将元素数量改为num,如果size()变大,则以elem的拷贝作为新元素的值
c.clear()//移除所有元素,将容器清空
异常处理
原则上deque提供的异常处理和vector提供的一样。新增的操作函数push_front(),pop_front()分别对应push_back()和pop_front()。C++标准库保证以下行为
如果push_back()和push_front()在安插元素时,发生异常,则该操作不会有任何作用
pop_back()和poo_front()不会抛出任何的异常
Deque运用演示
下面程序以简单的例子说明deque的功效
#include<iostream>
#include<deque>
#include<string>
#include<algorithm>
#include<iterator>
using namespace std;
int main(){
deque<string>coll;
coll.assign(3,string{"string"});
coll.push_back("last string");
coll.push_front("first string");
copy(coll.cbegin(),coll.cend(),ostream_iterator(cout,"\n"));
cout<<endl;
coll.pop_front();
coll.pop_back();
for(size_t i=1;i<coll.size();++i){
coll[i]="another "+coll[i];
}
coll.resize(4,"resized string ");
copy(coll.cbegin(),coll.cend(),ostream_iterator<string>(cout,"\n"));
}
输出结果如下
first string
string
string
last string
string
another string
another string
resized string