deque是“double-ended queue”的缩写,简称双端队列。
如下图所示,我们可以将deque与vector进行比较:
与string和vector类似,deque支持快速的随机访问。
同时,在deque的中间位置添加或删除元素的代价(可能)很高。
但是,在deque的两端添加或删除元素都是很快的。
定义和初始化
默认初始化deque对象
vector<T> v1; v1是一个空vector,它潜在的元素是T类型的,执行默认初始化。
值得注意的是,最常用的方式就是先定义一个空deque,然后当运行时获取到元素的值后,再逐一添加。
定义deque对象时指定元素的初始值
vector<T> v2(v1); v2中包含有v1所有元素的副本。
vector<T> v2 = v1; v2中包含有v1所有元素的副本。
当将一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。
创建指定数量的元素
deque<T> v3(n,val); v3中包含了n个重复的元素,每个元素的值都是val。
deque<T> v4(n); v4中包含了n个重复的元素,每个元素都是执行值初始化的对象。
通常情况下,可以只提供deque对象容纳的元素数量而略去初始值,此时标准库会创建一个值初始化的元素初值,并把它赋值给容器中的所有元素。
这个初值由deque对象中的元素的类型决定。
- 如果deque对象的元素是内置类型,比如int,则元素初始值自动设为0;
- 如果deque对象的元素是某种类类型,则元素由类默认初始化;
注意:
- 如果deque对象中元素的类型不支持默认初始化,我们必须提供初始化的元素值。
- 如果只提供了元素的数量而没有设定初始值,我们只能使用直接初始化。
列表初始化deque对象
deque<T> v5{a,b,c...}; v5包含了初始化值个数的元素,每个元素被赋予相应的初始值。
deque<T> v5 = {a,b,c...}; v5包含了初始化值个数的元素,每个元素被赋予相应的初始值。
C++11新标准提供了一种为deque对象的元素赋初值的方法——列表初始化。
此时,用花括号括起来的0个或多个初始元素值被赋予deque对象。
注意
在某些情况下,初始化的真实含义依赖于传递初始值用的是花括号{}还是圆括号()。
- 如果用的是圆括号,可以说提供的值是用来构造deque对象的。
- 如果用的是花括号,
- 可以表述成我们想列表初始化deque对象,即尽可能地把花括号内的值当成元素初始值的列表来处理。
- 在无法执行列表初始化时,我们考虑用这样的值来构造deque对象了。
deque<int > v1(10); /*v1有10个元素,每个值都是0*/
deque<int > v2{10}; /*v2有一个元素,值是10*/
deque<int > v3(10,1); /*v3有10个元素,每个值都是1*/
deque<int > v4{10,1}; /*v4有2个元素,值分别为10和1*/
deque<string> v5{"hi"}; /*列表初始化,v5有一个元素*/
deque<string> v6("hi"); /*错误*/
deque<string> v7(10); /*v7有10个默认初始化的元素*/
deque<string> v8{10,"hi"}; /*v8有10个值为“hi”的元素*/
向deque对象中添加元素
push_front函数
利用deque的成员函数push_front向deque队首添加元素。
push_front负责把一个值当成deque对象的首元素“压入”deque对象的“首端”。
deque<int > ideque;
for (size_t ix = 0; ix != 4 ; ++ix) {
ideque.push_front(ix);
}
for (int i = 0; i < 4; ++i) {
cout << ideque[i] << endl;
}
push_back函数
利用deque的成员函数push_back向deque队尾添加元素。
push_back负责把一个值当成deque对象的尾元素“压入”deque对象的“尾端”。
deque<int > v2;
for (int i = 0; i != 100; ++i) {
v2.push_back(i);
}
insert函数
利用deque的成员函数insert可以向容器中的任意位置插入0个或多个元素。
insert(p , t) 在迭代器p指向的元素之前插入一个值为t的元素
insert(p , n , t) 在迭代器p指向的元素之前插入n个值为t的元素
insert(p , b , e) 在迭代器p指向的元素之前插入迭代器b和e指定范围内的元素
insert(p , list) 在迭代器p指向的元素之前插入一个花括号包围的元素值列表
insert函数返回指向第一个新加入元素的迭代器。
deque<string> v = {"quasi" , "simba" , "frollo" , "scar"};
v.insert(v.begin() , "xiong");
v.insert(v.end() , 3 , "hupu");
v.insert(v.begin() , v.end()-2 , v.end());
v.insert(v.end() , {"these" , "words" , "will" , "go" , "at" , "the" , "end"});
for (int i = 0; i != 17; ++i) {
cout << v[i] << endl;
}
注意
刚接触C++语言的程序员也许会认为可以通过deque对象的下标形式来添加元素,事实并非如此。
关于下标运算必须明确一点:只能对确知已存在的元素执行下标操作。任何试图用下标的形式去访问一个不存在的元素将引发错误。
deque对象的下标运算符可用于访问已存在的元素,而不能用于添加元素。
从deque对象中删除元素
pop_front函数
利用deque的成员函数pop_front从deque中删除元素。
pop_front负责删除deque队首元素。
for (int j = 0; j != 50; ++j) {
v2.pop_front();
}
pop_back函数
利用deque的成员函数pop_back从deque中删除元素。
pop_back负责删除deque队尾元素。
for (int j = 0; j != 50; ++j) {
v2.pop_back();
}
erase函数
成员函数erase从容器中指定位置删除元素。
erase(p) 删除迭代器p所指定的元素
erase(b , e) 删除迭代器b和e所指定范围内的元素
返回指向删除的(最后一个)元素之后位置的迭代器。
deque<string> v = {"quasi" , "simba" , "frollo" , "scar"};
v.insert(v.begin() , "xiong");
v.insert(v.end() , 3 , "hupu");
v.insert(v.begin() , v.end()-2 , v.end());
v.insert(v.end() , {"these" , "words" , "will" , "go" , "at" , "the" , "end"});
v.erase(v.begin());
v.erase(v.begin() , v.begin() + 10);
for (int i = 0; i != 6; ++i) {
cout << v[i] << endl;
}
clear函数
删除容器中所有元素。
返回void。
访问deque对象的元素
在容器中访问元素的成员函数返回的都是引用!
- 如果容器时一个const对象,则返回值是const的引用。
- 如果容器不是const的,则返回值是普通引用,我们可以用来改变元素的值。
c.back() 返回尾元素的引用
c.front() 返回首元素的引用
c[n] 返回下标为n的元素的引用
c.at(n) 返回下标为n的元素的引用
deque<int > c = {1,2,3,4,5,6,7,8};
if (!c.empty())
{
c.front() = 42;
auto &v = c.back();
v = 1024;
auto v2 = c.back();
v2 = 0;
}
for (int i = 0; i < 8; ++i) {
cout << c[i] << endl;
}
注意
下标运算符接受一个下标参数,返回容器中该位置的元素的引用。
给定下标运算符必须在范围内(即,大于等于0,且小于容器的大小)。
保证下标有效是程序员的责任,下标运算符并不检查下标是否在合法范围内。使用越界的下标是一种严重的程序设计错误,而且编译器并不检查这种错误。
如果我们希望确保下标是合法的,可以使用at成员函数。
at成员函数类似于下标运算符,但如果下标越界,at会抛出一个out_of_range异常!
deque对象的大小操作
size函数
成员函数size返回deque对象中元素的个数。
empty函数
对成员函数empty而言,如果deque对象中不含有元素,返回真;否则,返回假。
max_size函数
返回一个大于或者等于该类型容器所能容纳的最大元素数的值。
deque对象的赋值
赋值运算符=
赋值元素符将其左边容器中的全部元素替换为右边容器中元素的拷贝。
如果两个容器原来大小不同,赋值运算后两者的大小都与右边容器的原大小相同。
c1 = c2; /*将c1的内容替换为c2中元素的拷贝*/
c1 = {a,b,c}; /*c1的大小为3*/
assign函数
顺序容器定义了一个名为assign的成员函数,允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。
assign操作用参数所指定的元素的拷贝替换左边容器中的所有元素。
seq.assign(b , e) 将seq中的元素替换为迭代器b和e所表示的范围中的元素
seq.assign(list) 将seq中的元素替换为列表初始化list中的元素
seq.assign(n , t) 将seq中的元素替换为n个值为t的元素
deque<string> vec ;
deque<string> vec1 = {"i" , "am" , "xxx" , "jjj" , "kkk"};
vec.assign(vec1.begin()+1 , vec1.end()-1);
vec.assign(10 , "hi");
for (int i = 0; i < 10; ++i) {
cout << vec[i] << endl;
}
swap函数
swap操作交换两个相同类型容器的内容。
调用swap之后,两个容器中的元素会交换。
容器既提供了成员函数版本的swap,也提供了非成员函数版本的swap。建议使用非成员版本的swap。
deque<string> svec1(10);
deque<string> svec2(24);
swap(svec1,svec2);
改变容器大小
c.resize(n) 调整c的大小为n个元素
c.resize(n,t) 调整c的大小为n个元素,任何新添加的元素都初始化为值t
我们可以用resize来增大或缩小容器。
如果当前大小大于所要求的大小,容器后部的元素会被删除。如果当前大小小于新大小,会将新元素添加到容器后部。
deque<int > ilist(10,42);
ilist.resize(15); /*将5个值为0的元素添加到ilist的末尾*/
ilist.resize(25,-1); /*将10个值为-1的元素添加到ilist的末尾*/
ilist.resize(5); /*从ilist末尾删除20个元素*/