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個元素*/