1. 程式人生 > >C++容器,迭代器,容器的介面卡

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的元素,返回刪除的個數,不存在返回0
  • c.erase(p):刪除迭代器指向的元素,必須是內部的,不能是end()
  • c.erase(b,e):刪除區間內的元素

map的下標操作

下標操作只適用於mapunordered_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;
}

迭代器

迭代器是一個泛型的指標型別,只要是可迭代的容器,都可以使用迭代器。介面卡不可以使用,比如stackqueuepriority_queue等的資料型別。

  • begin()是首元素
  • end()是尾後元素
  • 若容器是空的,則begin()==end()

迭代器的標準運算成員:

  • *iter取內容
  • iter->item取出內部的成員
  • ++iter指向下一個元素
  • --iter指向上一個
  • iter1==iter2判斷是否指向同一個元素
  • iter1!=iter2不指向同一個元素的判定

使用了迭代器的迴圈體,不要向迭代器所屬的容器新增元素!!!

非標準迭代器成員的運算子號根據具體的容器確定。

容器的介面卡

容器的介面卡是一個機制,使得某種事物看起來像另一種事物。給出基本型別和操作:

  • size_type:保證當前型別的最大物件的大小
  • value_type:元素的型別
  • container_type:實現介面卡底層容器的型別
  • A a:建立一個A型別的空介面卡a
  • A a(c):建立一個名為a的介面卡,帶有容器c的一個拷貝!!!
  • a.empty():判斷是否是空,空返回true,否則是false
  • a.size():返回元素的個數
  • swap(a,b):交換a,b的內容,要求比素食同類型的元素,底層容器型別也必須相同
  • a.swap(b):同上

棧預設使用deque作為底層,佇列預設使用dequepriority_queue預設使用vector作為底層。也可以自定義底層容器。需要注意的是:介面卡不能進行迭代!!