C++容器,迭代器,容器的介面卡
容器
順序容器
主要靠下標和迭代器進行操作。順序性的主要靠下標,鏈式的靠迭代器訪問。
包含了順序型的容器和鏈式的容器。
連續型的包括:
vector
:向量,可以快速擴充套件和刪除元素,在隊尾的操作有優勢!deque
:雙端佇列,可以快速的從隊首和隊尾新增或者刪除元素,適合雙向的操作。array
:陣列,大小必須是固定的。string
:字串
鏈式的包括:
list
:雙向連結串列forward_list
:單向連結串列。沒有size
操作。
關聯容器
簡介
主要靠關鍵字進行訪問。關聯容器主要是分為兩類:set
型別的和map
型別的。同時也可以按照有序和無序分類,也可以按照順序儲存和無序儲存分類。
總共有8中型別額關聯容器:
map
:關鍵字-值,關鍵字唯一set
:關鍵字,關鍵字唯一multimap
:關鍵字-值,關鍵字可以重複multiset
:關鍵字:關鍵字可以重複unordered_map
:雜湊函式組織的map
,關鍵字唯一unordered_set
:雜湊函式組織的set
,關鍵字唯一unordered_multimap
:雜湊函式組織的multimap
,關鍵字不唯一unordered_multiset
:雜湊函式組織的multiset
,關鍵字不唯一
上述以unordered
開頭的都是無序的關聯容器。關聯容器都是支援模板型別操作的。
關聯容器不支援順序容器的那種與位置有關的操作,比如push_back
push_front
等,這些操作無意義。關聯容器的迭代是雙向的。
有序關聯容器的關鍵字必須定義比較函式,否則無法放入容器,因為容器需要靠<
比較函式才能進行資料結構的構建。使用有序容器儲存自定義的資料型別時,除了給出資料型別的模板之外,還要給出自定義的操作型別。不過我們一般都是過載運算子<
進行操作。
#include <iostream>
#include <set>
using namespace std;
class Data1{
public:
Data1(int _a,int _b=0,int _c=0):a(_a),b(_b),c(_c){}
bool operator< (const Data1 &d){
return a<d.a;
}
int a,b,c;
};
int main(){
set<Data1>s1;
return 0;
}
pair
資料型別,存在於utility
標頭檔案。該資料型別用於map
大類的操作,具體有一下幾個操作:
pair<T1, T2> p;
宣告一個pair
pair<T1, T2> p(v1, v2);
宣告並初始化pair<T1, T2>p{v1, v2} ;
宣告並初始化auto p=make_pair(v1,v2);
初始化,編譯器自動推斷所有型別。p.first
:返回鍵值p.second
:返回對應的資料p1 < p2
:比較的是鍵值,對於其他的運算子同理。
主要操作
幾個特性:
key_type
:容器的關鍵字型別mapped_type
:每個關鍵字關聯的型別,只適用於map
型別value_type
:資料型別- 對於
set
型別,這與key_type
相同 - 對於
map
型別,為pair<const key_value, mapped_type>
- 對於
上述說的型別是大類。
迭代操作
一般使用自動推導得到迭代器,迭代器是value_type
型別的指標。
對於set
來說,就是元素模板的型別;對於map
來說,是pair<const key_value, mapped_type>
型別。
注意,set
的迭代器是const
型別的,只能讀不能修改,這很好理解,因為一旦修改鍵值,所有的排列順序都要進行改變;而map
的不能修改鍵值,但是可以修改鍵值對應的資料:
#include <iostream>
#include <map>
using namespace std;
int main(){
map<int,string>m{{1,"a"},{2,"b"}};
for(auto it=m.begin();it!=m.end();it++){
it->second="new"; // 在這裡修改對應的資料
}
cout<<m[1]<<endl; // 輸出new
return 0;
}
對於迭代操作,只要是有序容器,都是按照字典序輸出的。
一般來說,關聯容器的迭代器都是隻讀型別的,我們平時用的sort
等的演算法都不適用這種容器,但是可以使用find
的成員給鍵值定位。也可以使用copy
進行復制操作。
插入操作
單個鍵值的插入:
使用內建的insert
成員進行操作。幾個常用的操作:
c.insert(v)
,插入對應的valut_type
型別的資料c.emplace(args)
,對於單鍵值的關聯容器,只有元素不存在的時候進行插入,多鍵值的直接插入;返回一個pair
型別的資料,第一個是指向含有關鍵字的元素,第二個是bool
,表示是否插入成功。c.insert(b,e)
b和e是迭代器,表示插入一個區間的value_type
型別資料c.insert(il)
表示使用花括號的型別
上述的返回值都是一個pair
,作用同上。
多個鍵值的插入:
不返回任何資料,因為總是可以進行資料插入。
刪除操作
使用erase
進行刪除操作:
c.erase(k)
:刪除關鍵字為k
的元素,返回刪除的個數,不存在返回0c.erase(p)
:刪除迭代器指向的元素,必須是內部的,不能是end()
c.erase(b,e)
:刪除區間內的元素
map的下標操作
下標操作只適用於map
和unordered_map
,不能用於多鍵值的;同時,元素必須是非const
型別的。set
沒有下標操作。兩種資料型別:
c[k]
:返回關鍵字是k
的元素,如果k
不存在,進新增這個關鍵字,並初始化c.at(k)
:返回關鍵為k
的元素,不存在丟擲out_of_range
異常
上述返回的都是mapped_type
型別的資料。
訪問操作
c.find(k)
:返回第一個迭代器,指向關鍵字是k
的,不存在返回end()
c.count(k)
:返回關鍵字為k
的個數c.lower_bound(k)
:返回第一個迭代器,指向第一個關鍵字不小於k
元素,不適於無序容器!c.upper_bound(k)
:返回第一個迭代器,指向第一個關鍵字大於k
的元素,不適於無序容器!c.equal_range(k)
:返回一個pair
,表示範圍。如果不存在,成員均為end()
無序容器
無序容器使用hash
函式和==
進行運算。主要處理元素沒有明顯有序關係的情況,理論上有更好的效能,但是實際上需要多次進行工程驗證效率。
無序容器的插入刪除和有序容器基本一致,只是沒有和序列有關的操作。無序容器使用了一個鏈式的桶狀結構,效能取決於雜湊函式的質量和桶的數量與大小。如果是自定義的資料型別,我們需要自己來說明雜湊函式,否則不能直接使用。
#include <iostream>
#include <unordered_set> // 注意標頭檔案
#include <string>
#include <functional>
using namespace std;
class Data{
public:
Data(int a,string s):n(a),str(s){}
int n;
string str;
};
size_t hasher(const Data &d){
return hash<string>()(d.str);
}
bool cmp(const Data& d1, const Data &d2){
return d1.str==d2.str;
}
int main(){
using SD_multiset=unordered_multiset<Data,
decltype(hasher)*, decltype(cmp)*>;
// 桶的大小,雜湊函式,比較函式
SD_multiset data_set(10,hasher,cmp);
Data d(1,"hello");
data_set.insert(d);
return 0;
}
迭代器
迭代器是一個泛型的指標型別,只要是可迭代的容器,都可以使用迭代器。介面卡不可以使用,比如stack
、queue
和priority_queue
等的資料型別。
begin()
是首元素end()
是尾後元素- 若容器是空的,則
begin()==end()
迭代器的標準運算成員:
*iter
取內容iter->item
取出內部的成員++iter
指向下一個元素--iter
指向上一個iter1==iter2
判斷是否指向同一個元素iter1!=iter2
不指向同一個元素的判定
使用了迭代器的迴圈體,不要向迭代器所屬的容器新增元素!!!
非標準迭代器成員的運算子號根據具體的容器確定。
容器的介面卡
容器的介面卡是一個機制,使得某種事物看起來像另一種事物。給出基本型別和操作:
size_type
:保證當前型別的最大物件的大小value_type
:元素的型別container_type
:實現介面卡底層容器的型別A a
:建立一個A型別的空介面卡aA a(c)
:建立一個名為a的介面卡,帶有容器c
的一個拷貝!!!a.empty()
:判斷是否是空,空返回true
,否則是false
a.size()
:返回元素的個數swap(a,b)
:交換a,b的內容,要求比素食同類型的元素,底層容器型別也必須相同a.swap(b)
:同上
棧預設使用deque
作為底層,佇列預設使用deque
,priority_queue
預設使用vector
作為底層。也可以自定義底層容器。需要注意的是:介面卡不能進行迭代!!