1. 程式人生 > >C++ 順序容器 筆記整理

C++ 順序容器 筆記整理

順序容器

順序容器型別

vector 可變大小陣列.支援快速隨機訪問.在尾部之外的位置插入or刪除元素可能很慢.

deque 雙端佇列. 支援快速隨機訪問. 從頭尾位置插入or刪除元素很快.

list 雙向連結串列. 只支援雙向順序訪問. 在`list中任何位置進行插入or刪除操作都比較快.

array 固定大小陣列.支援快速隨機訪問. 不能新增or刪除元素.

stringvector 類似. 隨機訪問快.在尾部插入or刪除快.

容器操作

// 建構函式
C c;                // 預設建構函式,構造空容器
C c1(c2);           // 構造c2的拷貝c1
C c(b,e); // 構造c,將迭代器b和e指定範圍內的元素拷貝到c (array不支援) C c{a,b,c}; // 列表初始化{} // 賦值與swap c1=c2; // 將c1中的元素替換c2中的元素 c1={a,b,c...}; // 將c1中的元素替換為列表中的元素! (array不支援) a.swap(b); // 交換a,b的元素 swap(a,b); // 與a.swap(b)等價 // 大小 c.size(); // c中元素的數目 (不支援 forward_list) c.max_size(); // c可儲存的最大元素數目
c.empty(); // 若c中儲存元素,返回false.否則為true // 新增/刪除元素 (不適用於array) // 注:在不同容器中,這些操作的介面都不同 c.insert(args); // 將args中的元素拷貝進c c.emplace(inits); // 使用inits構造c中的一個元素 c.erase(args); // 刪除args指定元素 c.claer(); // 刪除c中所有元素,返回void // 獲取迭代器 c.begin();c.end(); // 返回指向c的首元素和尾元素之後位置的迭代器 c.cbegin();c.cend();// 返回const_iterator
// 關係運算符 == != // 所有容器都支援的相等/不等 運算子 <,<=,>,>= // 關係運算符 (無序關聯容器不支援) // 反向容器的額外成員(不支援forward_list) reverse_iterator; // 按逆序定址元素的迭代器 const_reverse_iterator; // 不能修改元素的逆序迭代器 c.rbegin();c.rend(); // 返回指向c尾元素和首元素之前位置的迭代器 c.cebegin();c.crend(); // 返回const_reverse_iterator

迭代器

  • 迭代器範圍由一對迭代器表示.

  • 左閉右開 區間!

    begin 與 end 相等,範圍為空

  • 不需要寫訪問時,應使用 cbegincend

容器定義與初始化

C c; // 預設建構函式.若是一個array,則c中元素按預設方式初始化.否則為空.
C c1(c2); // c1初始為為c2的拷貝.注意! 型別相同(容器型別,元素型別).對於array,大小還必須相同.
C c1=c2; 
C c{a,b,c}; // 初始化為初始化列表中元素的拷貝.
C c={a,b,c};
C c(b,e); // 迭代器初始化.c初始化為b和e指定範圍中的元素的拷貝.

/* 只有順序容器(不包括array)的構造器才能大小引數 */
C seq(n); // seq包含n個元素.這些元素進行了初始化.
C seq(n,t); // 初始化了n個元素.值都為t.

賦值與swap

  • 賦值相關運算會導致指向 左邊容器 的內部迭代器,引用和指標失效!
  • swap操作將容器內部內容交換 不會 導致指向容器的迭代器,引用和指標失效.(容器型別為array和string的情況除外)
c1=c2; // c1中的元素替換為c2中的元素.
C={a,b,c}; // array不支援
swap(c1,c2); 
c1.swap(c2);
  • assign操作不適用於關聯容器和array! 僅順序容器!
seq.assign(b,e);    // 迭代器b,e不能指向seq中的元素
// 由於舊元素被替換掉了,傳遞給assign的不能是呼叫的assign的容器.
seq.assign(il);     // 替換為初始化列表 il 中的元素
seq.assign(n,t);    // 替換為n個t值的元素
  • 統一使用非成員版本的swap是一個好習慣. swap(a,b);

向順序容器新增元素

  • 順序容器與關聯容器的不同之處在於 二者組織元素的方式.

  • 這些不同之處直接關係到元素如何 儲存,訪問,新增,刪除

  • 非常重要的一點. 除了 forward_list 其他的順序容器都是前插!!!

// 這些操作會改變容器的大小.array不支援!
c.push_bacck(t);            // 在c的 尾部 建立一個值為t 或由args建立 的元素.
c.emplace_back(args);   
c.push_front(t);            // 在c的 頭部 建立一個值為t 或由args建立 的元素.
c.emplace_front(args);
c.insert(p,t);              // 在迭代器p指向的元素 之前 新增一個元素.
c.emplace(p,args);
c.insert(p,n,t);            // 在p指向的元素 之前 新增 n個 值為t的元素
c.insert(p,b,e);            // 將迭代器b和e指定範圍的元素插入到 p指向的元素之前!
c.insert(p,il);             // il是一個花括號包圍的 元素值列表 ,將這些值插入到p指向元素之前.
  • forward_list 有自己專有版本的 insertemplace ;

  • forward_list 不支援 push_backemplace_back ;

  • vectorstring 不支援 push_frontemplace_front ;

  • 向一個 vector ,stringdeque 插入元素會使所有指向容器的迭代器,引用,指標失效.

  • vector不支援 push_front .但是 我們可以插入到 begin() 之前!!!

    svec.insert(sevc.begin(),"hello!"); // 相當於push_front()
  • 將元素插入到 vector,deque,string 中的任何位置是合法的, 但很耗時.

  • 使用 insert 的返回值,可以容器中的特定一個位置反覆的插入元素:

    list<stirng> lst;
    auto iter = lst.begin();
    while(cin>>word)
    iter = lst.insert(iter,word);   //等價與 push_front
  • emplace 函式是在容器中 直接構造 元素.傳遞給 emplace 函式的引數必須與 元素型別的建構函式 相匹配.

訪問元素

c.back();           // 返回c中尾元素的引用;
c.front();          // 返回c中頭元素的引用;
c[n];               // 返回下標為n的元素的引用.n是一個無符號整數
c.at(n);            // 返回下標為n的元素的引用. 若越界!會丟擲一個 out_of_range的異常
  • at下標操作 只適用於 string vector deque array

  • back 不適用於 forward_list

  • 訪問成員函式返回的是 引用

    若容器是一個 const 物件,則返回的是 const 的引用;

    否則,普通引用,可用來改變元素的值;

  • 確保下標訪問合法,可以使用 at 成員函式

  • *iter 解引用迭代器也可以訪問元素

刪除元素

  • 刪除元素會改變容器大小,不適用於 array
// 刪除元素會改變容器大小,不適用於 array
c.pop_back();           // 刪除c中尾元素,返回void
c.pop_front();          // 刪除c中首元素,返回void
c.erase(p);             // 刪除迭代器p指向的元素,返回一個指向被刪除元素 之後 的迭代器!!!
c.erase(b,e);           // 刪除b和e所指定範圍的元素,返回一個指向最後一個被刪除元素 之後 的迭代器!!!
c.clear();              // 刪除c中所有元素,返回void
  • forward_list 有特殊版本的 erase

  • forward_list 不支援 pop_back() ;

  • vectorstring 不支援 pop_front() ;

  • 刪除 deque除首尾之外 的任何元素都會使所有迭代器,引用和指標失效

  • 指向vectorstring刪除點之後 的迭代器,引用,指標都會失效.

  • 刪除元素的成員函式並不會檢查其引數! 在刪除元素之前,請確保它們存在

  • pop_frontpop_back 成員函式返回void,若需要彈出的值! 必須在執行彈出操作前儲存它!!!

    使用 front() / back()

  • erase 函式返回的是 指向刪除的 最後一個元素之後 位置的迭代器

特殊的 forwartd_list 操作

  • 為什麼 forward_list 需要特殊版本的新增和刪除操作呢?

    對於單鏈表來說.新增或刪除一個元素.它前面的元素內容也會發生變化.

    為了刪除或新增一個元素.必須訪問到它的前驅元素.

    在一個forward_list中新增or刪除元素的操作都是通過改變 給定元素之後的元素來完成的

  • 首前 迭代器: before_begin

  • forward_list 在添刪元素之前,我們必須關注兩個迭代器! 指向我們要處理的元素 and 指向其前驅

lst.before_begin();         // 返回指向連結串列首元素之前 不存在元素 的迭代器,不能解引用!
lst.cbefore_begin();
list.insert_after(p,t);     // 在迭代器p之後 的位置插入元素,t是一個物件!!!
list.insert_after(p,n,t);   // n 是數量
list.insert_after(p,b,e);   // b,e表示範圍的一對迭代器
list.insert_after(p,il);    // il是一個花括號列表.
                            // 返回 一個指向最後一個插入的元素之後的迭代器
emplace_after(p,args);      // 使用args在p指定位置之後 建立一個元素.返回一個指向這個新元素的迭代器!
lst.erase_after(p);         // 刪除p指向位置之後的元素!
lst.erase_after(b,e);       // 刪除從b之後(不包含b)到e之間的元素!
                            // 返回 一個指向被刪元素之後元素的迭代器!

改變容器大小

// resize 來增大或減小容器.
c.resize(n);        // 調整c的大小為n個元素.
c.resize(n,t);      // 調整c的大小為n.任何 新新增的 元素值為t
  • resize 不支援 array
  • resize 縮小容器,則指向被刪除元素的迭代器,引用,指標都會失效!
  • vector stirng deque 進行 resize 可能導致迭代器,指標,引用失效

  • 關於容器大小的操作.

    成員函式size(); // 返回當前容器中元素的個數
    成員函式empty(); // 當size為0時返回真,否則返回假
    成員函式max_size(); // 返回一個大於或等於該型別容器所能容乃的最大元素的值
  • 關係運算符

    比較兩個容器實際上是進行元素的逐對比較.

vector 物件是如何增長的

  • 元素必須是連續儲存的.如沒有新空間容納元素,容器必須 分配新的記憶體空間儲存已有元素和新元素 將已有元素從舊位置移動到新空間中,然後新增新元素,釋放舊空間!!

  • 為了避免上一條代價,採用 減少容器空間重新分配次數的策略 !!! 通常會分配比新空間要求更大的記憶體空間!

  • 管理容量的成員函式

    // shrink_to_fit 只適用於 vector string deque
    // capacity 和 reserve 只適用於 vector string
    c.shrink_to_fit();    // 請將capacity() 減少為 size() 相同大小
    c.capacity();     // 不重新分配記憶體空間的話,c可以容納多少元素
    c.reserve(n);     // 分配至少容納n個元素的記憶體空間
  • reserve 並不改變容器中元素的數量,僅影響 vector 預先分配多大的記憶體空間

  • 只有當需要的記憶體空間超過當前容量時reserve 呼叫才會改變 vector 的容量

  • capacity 是指不重新分配記憶體空間下,可以容納多少元素; size 是指它已經儲存的元素的數目;

  • 每個 vector 實現都可以選擇自己記憶體分配策略,原則: 只有當迫不得已時才會分配新的記憶體空間

額外的 string 操作

除了順序容器共同的操作外.string型別還提供了額外的一些操作.如

  • string類與C風格字元陣列之間的轉換
  • 增加下標替代迭代器的版本
  • 大量函式

構造 string 的其他方法

// n len2 和 pos2 都是無符號值
string s(cp,n);         // s是cp指向的陣列中前n個字元的拷貝.此陣列至少應該包含n個字元
string s(s2,pos2);      // s是string s2 從下標pos2開始的字元的拷貝
string s(s2,pos2,len2); // s是string s2 從下標pos2開始的len2個字元的拷貝
  • 通常,當從一個 const char* 建立 string 時,指標指向的陣列必須是 以空字串結尾的 ,拷貝操作遇到 空字元 停止!!!
  • 若傳遞給建構函式一個 計數值 ,陣列 不必以空字元結尾.

  • 若未傳遞 計數值 且 未以空字元結尾.建構函式的行為 未定義

    const char *cp = "hello world!!!";    // 是以空字元為結尾
    char noNULL[] = {'H','i'};            // 沒有空字元結尾
    string s1(cp);            // s1=="hello world!!!"
    string s2(noNULL,2);  // s2=="Hi" // 因為有len.

substr 操作!!!

  • substr 返回一個 string 其實原始字串或部分字串的拷貝.
s.substr(pos,n);    // 返回一個stirng,包含spos開始的n個字元的拷貝.pos預設為0,n預設為s.size();
// n是大小,長度!!!

改變 string 的其他操作

除了順序容器的賦值運算子, assign , insert , erase 操作

  • 接受下標版本的 insert和erase版
  • C風格字串陣列的insert和assign
  • append 和 replace 函式
s.insert(pos,args);         // 在pos之前!!! 插入args指定的字元.pos可以是下標or迭代器.
                            // 接受下標的返回一個指向s的引用; 接受迭代器返回指向 第一個插入字元 的迭代器
s.erase(pos,len);           // 刪除從pos開始的len個字元.若len被省略,刪除從pos開始到結尾,返回s的一個引用
s.assign(args);             // 將s中的字元替代為args指定的字元.返回一個指向s的引用
s.append(args);             // 將args追加到s.返回一個指向s的引用
s.replace(range,args);      // 刪除s中範圍range內的字元,替換為args指定字元.
                            // range: 1.一個下標和一個長度 2. 一對指向s的迭代器
  • append assign insert replace 函式有多個過載版本
  • 並不是每個函式都支援所有形式的引數

string 搜尋操作

  • 搜尋操作返回的是一個 string::size_type 值,表示 匹配發生位置的下標
  • 搜尋失敗,返回的是 string::npos
s.find(args);               // 查詢s中args第一次出現的位置
s.rfind(args);              // 查詢s中args最後一次出現的位置
s.find_first_of(args);      // 在s中查詢args中任一字元第一次出現的位置
s.find_last_of(args);       // 在s中查詢args中任一字元最後一次出現的位置
s.find_first_not_of(args);  // 在s中查詢第一個不在args中的字元
s.find_last_not_of(args);   // 在s中查詢最後一個不在args中的字元

//args的引數形式:
c,pos;          // 從s位置pos開始查詢字元c.pos預設為0
s2,pos;         // 從s中查詢s2字串
cp,pos;         // 從s中pos位置開始查詢 指標cp指向的以空字元結尾的C風格字串.pos預設為0
cp,pos,n;       // 從s中pos位置開始查詢cp指向的陣列的前n個字元.pos 和 n 無預設值

compare 函式

  • 標準庫string提供一組 compare 函式,這些函式與C標準庫的 strcmp 函式相似
  • s.compare 返回0 正數 負數
// s.compare()的幾種引數形式
s2;                 // 比較s和s2
pos1,n1,s2;         // 將s中從pos1開始的n1個字元 與 s2比較
pos1,n1,s2,pos2,n2; // 將s中從pos1開始的n1個字元 與 s2從pos2開始的n2個字元 比較

cp;                 // 比較s與cp所指向的以空字元結尾的字元陣列
pos1,n1,cp;         // 將s中從pos1開始的n1個字元 與 cp所指向的以空字元結尾的字元陣列進行比較
pos1,n1,cp,n2;      // 將s中從pos1開始的n1個字元 與 cp所指向的地址開始的n2字元 比較

數值轉換

// string和數值之間的轉換
to_string(val);
stoi(s,p,b);            // 返回s的起始子串(整數的內容)數值,b表示轉換所用的基數.預設是10;p是size_t指標.
stol(s,p,b);
stoul(s,p,b);
stoll(s,p,b);
stoull(s,p,b);
stof(s,p);              // f返回s的起始子串(表示浮點數內容)的數值.
stod(s,p);
stold(s,p);
  • 若string不能轉換為一個數值,這些函式丟擲一個 invalid_argument 異常
  • 若 轉換得到的數值 無法用任何型別表示,丟擲一個 out_of_range 異常

重點!!!

to_string(val); // 將數值轉換成 string啊!!!

stoi(s); // 將string 轉成 int

容器介面卡

  • 除了順序容器外,標準庫還定義了三個 順序容器介面卡
  • stack queue priority_queue 棧 / 佇列 / 優先佇列(堆)
  • 介面卡 是一種機制!使得某種事物的行為看起來像另一個.
// 所有容器介面卡 都支援的操作與型別
size_type;              // 一種型別,足以儲存當前型別的最大物件的大小
value_type;             // 元素型別
container_type;         // 實現介面卡的底層容器型別
A a;                    // 建立一個名為a的介面卡
A a(c);                 // 建立一個名為a的介面卡,帶有容器c的一個拷貝
關係運算符;              // == != < <= > >=
a.empty();              // 若a包含任意元素,返回false.否則 true
a.size();               // 返回a中的元素數目
swap(a,b);              // 交換a b 內容,ab型別相同,包括底層容器的型別
a.swap(b);          
  • 所有的介面卡都要求容器具有新增 刪除元素的能力
  • 介面卡不能構造在array之上,也不能用forward_list來構造介面卡

棧介面卡

// 棧操作
s.pop();            // 刪除棧頂元素,但不返回該元素值!!!
s.push(item);       // 建立一個新元素 壓入棧頂
s.emplace(args);
s.top();            // 返回棧頂元素
  • 只能使用介面卡操作! 不能使用底層容器型別的操作
  • 棧 預設是基本 deque 實現的,也可以在 listvector 上實現

佇列介面卡

// queue 和 priority_queue 操作
q.pop();            // 返回queue的首元素 或 priority_queue的最高優先順序元素,但不能刪除此元素
q.front();          // 返回首元素 or 尾元素,但不能刪除此元素
q.back();           // 只適用於 queue! 
q.top();            // 返回最高優先順序元素,但不能刪除此元素,只適用於 priority_queue
q.push(item);       // 在queue末尾 或 priority_queue中恰當的位置建立一個元素,其值為tiem,或由args構造
q.emplace(args);    
  • queue 預設基於 deque 實現. 也可以用 list vector 實現
  • priority_queue 預設基於 vector 實現. 也可以用 deque 實現
  • priority_queue 允許我們為佇列中的元素建立優先順序.新加入的元素會排列在所有優先順序比它低的已有元素之前!!!