《C++ Primer》筆記 第9章 順序容器

  1. 順序容器類型
類型 解釋
vector 可變大小數組。支持快速隨機訪問。在尾部以外的位置插入或刪除元素可能很慢
deque 雙端隊列。支持快速隨機訪問。在頭尾位置插入、刪除速度很快
list 雙向鏈表。只支持雙向順序訪問。在list中任何位置進行插入、刪除操做速度都很快
forward_list 單向鏈表。只支持單向順序訪問。在鏈表任何位置進行插入、刪除操做速度都很快
array 固定大小數組。支持快速隨機訪問。不能添加或刪除元素
string 與vector類似的容器,但專門用於保存字符。隨機訪問快。在尾部插入、刪除速度快。
  1. stringvector將元素保存在連續的內存空間中。由元素的下標來計算其地址是很是快速的,但在中間位置添加或刪除元素就會很是耗時。listforward_list兩個容器的設計目的是令容器任何位置的添加和刪除操做都很快速,做爲代價,這兩個容器不支持元素的隨機訪問,而且相比vector、deque、array,這兩個容器的額外內存開銷也很大。deque支持快速的隨機訪問,在deque的兩端添加或刪除元素都是很快的,與list或forward_list添加刪除元素的速度至關。array對象的大小是固定的。所以,array不支持添加和刪除元素以及改變容器大小的操做。forward_list的設計目標是達到與最好的手寫的單向鏈表數據結構至關的性能。所以,forward_list沒有size操做,由於保存或計算其大小就會比手寫鏈表多出額外的開銷。對其餘容器而言,size保證是一個快速的常量時間的操做。
  2. 如下是一些選擇容器的基本原則:
    • 除非你有很好的理由選擇其餘容器,不然應使用vector。
    • 若是你的程序有不少小的元素,且空間的額外開銷很重要,則不要使用list或forward_list。
    • 若是程序要求隨機訪問元素,應使用vector或deque。
    • 若是程序要求在容器的中間插入或刪除元素,應使用list或forward_list。
    • 若是程序須要在頭尾位置插入或刪除元素,但不會在中間位置進行插入或刪除操做,則使用deque。
    • 若是程序只有在讀取輸入時才須要在容器中間位置插入元素,隨後須要隨機訪問元素,則
      • 首先,肯定是否真的須要在容器中間位置添加元素。當處理輸入數據時,一般能夠很容易地向vector追加數據,而後再調用標準庫的sort函數來重排容器中的元素,從而避免在中間位置添加元素。
      • 若是必須在中間位置插入元素,考慮在輸入階段使用list,一旦輸入完成,將list中的內容拷貝到一個vector中。
  3. 若是你不肯定應該使用哪一種容器,那麼能夠在程序中只使用vector和list公共的操做:使用迭代器,不使用下標操做,避免隨機訪問。這樣,在必要時選擇使用vector或list都很方便。
  4. 容器類型上的操做造成了一種層次:
    • 某些操做是全部容器類型都提供的。
    • 另一些操做僅針對順序容器、關聯容器或無序容器。
    • 還有一些操做只適用於一小部分容器。
  5. 順序容器構造函數的一個版本接受容器大小參數,它使用了元素類型的默認構造函數。但某些類沒有默認構造函數。咱們能夠定義一個保存這種類型對象的容器,但咱們在構造這種容器時不能只傳遞給它一個元素數目參數:
    // 假定noDefault是一個沒有默認構造函數的類型
      vector<noDefault> v1(10, init); // 正確:提供了元素初始化器
      vector<noDefault> v2(10); // 錯誤:必須提供一個元素初始化器
  6. 容器操做
類型別名 解釋
iterator 此容器類型的迭代器類型
const_iterator 能夠讀取元素,但不能修改元素的迭代器類型
size_type 無符號整數類型,足夠保存此種容器類型最大可能容器的大小
difference_type 帶符號整數類型,足夠保存兩個迭代器之間的距離
value_type 元素類型
reference 元素的左值類型;與value_type&含義相同
const_reference 元素的const左值類型(即,const value_type&
構造函數 解釋
C c; 默認構造函數,構造空容器
C c1(c2); 構造c2的拷貝c1
C c(b,e); 構造c,將迭代器b和e指定的範圍內的元素內的元素拷貝到c
C c{a,b,c...}; 列表初始化c
賦值與swap 解釋
c1=c2 將c1中的元素替換爲c2中元素
c1={a,b,c...} 將c1中的元素替換爲列表中元素
a.swap(b) 交換a和b的元素
swap(a,b) 與a.swap(b)等價
大小 解釋
c.size() c中元素的數目
c.max_size() c可保存的最大元素數目
c.empty() 若c中儲存了元素,返回false,不然返回true
添加、刪除元素 解釋
c.insert(args) 將args中的元素拷貝進c
c.emplace(inits) 使用inits構造c中的一個元素
c.erase(args) 刪除args指定的元素
c.clear() 刪除c中的全部元素,返回void
關係運算符 解釋
==,!= 全部容器都支持相等(不等)運算符
<,<=,>,>= 刪除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.crbegin(),c.crend() 返回const_reverse_iterator
  1. forward_list迭代器不支持遞減運算符(--)。ios

  2. 一個迭代器範圍由一對迭代器表示,兩個迭代器分別指向同一個容器中的元素或者是尾元素以後的位置。c++

  3. 迭代器範圍中的元素包含first所表示的元素以及從first開始直至last(但不包含last)之間的全部元素。左閉合區間:[left, right)。git

  4. 若是知足以下條件,兩個迭代器begin和end構成一個迭代器範圍:程序員

    • 它們指向同一個容器中的元素,或者是容器最後一個元素以後的位置,且
    • 咱們能夠經過反覆遞增begin來到達end。換句話說,end不在begin以前。
  5. 標準庫使用左閉合範圍是由於這種範圍有三種方便的性質。假定begin和end構成一個合法的迭代器範圍,則編程

    • 若是begin與end相等,則範圍爲空
    • 若是begin與end不等,則範圍至少包含一個元素,且begin指向該範圍中的第一個元素
    • 咱們能夠對begin遞增若干次,使得begin==end
  6. 這些性質意味着咱們能夠用循環來處理一個元素範圍。數組

    while (begin != end)
    {
        *begin = val; // 正確:範圍非空,所以begin指向一個元素
        // 在while循環中,能夠安全地解引用begin,由於begin必然指向一個元素。
        ++begin; // 移動迭代器,獲取下一個元素
    }
  7. 反向迭代器就是一種反向遍歷容器的迭代器,與正向迭代器相比,各類操做的含義也都發生了顛倒。例如,對一個反向迭代器執行++操做,會獲得上一個元素。安全

  8. begin和end有多個版本:帶r的版本返回反向迭代器;以c開頭的版本則返回const迭代器;不以c開頭的函數都是被重載過的。一個是const成員,返回容器的const_iterator類型;另外一個是很是量成員,返回容器的iterator類型。當咱們對一個很是量對象調用這些成員時,獲得的是返回iterator的版本。只有在對一個const對象調用這些函數時,纔會獲得一個const版本。與const指針和引用相似,能夠將一個普通的iterator轉換爲對應的const_iterator,但反之不行。數據結構

  9. 當auto與begin或end結合使用時,得到的迭代器類型依賴於容器類型,與咱們想要如何使用迭代器絕不相干。但以c開頭的版本仍是能夠得到const_iterator的,而無論容器的類型是什麼。當不須要寫訪問時,應該使用cbegin和cend。app

  10. 容器定義和初始化函數

方法 解釋
C c; 默認構造函數。若是c是一個array,則c中元素按默認方式初始化;不然c爲空
C c1(c2) c1初始化爲c2的拷貝。c1和c2必須是相同類型(即,它們必須是相同的容器類型,且保存的是相同的元素類型;對於array類型,二者還必須具備相同大小)
C c{a,b,c...}或C c={a,b,c...} c初始化爲初始化列表中元素的拷貝。列表中元素的類型必須與c的元素類型相容。對於array類型,列表中元素數目必須等於或小於array的大小,任何遺漏的元素都進行值初始化
C c(b,e) c初始化爲迭代器b和e指定範圍中的元素的拷貝。範圍中元素的類型必須與C的元素的類型相容(array不適用)
—— 只有順序容器(不包括array)的構造函數才能接受大小參數
C seq(n) seq包含n個元素,這些元素進行了值初始化;此構造函數是explicit的。(string不適用)
C seq(n,t) seq包含n個初始化爲值t的元素
  1. 當將一個容器初始化爲另外一個容器的拷貝時,兩個容器的容器類型和元素類型都必須相同。但當傳遞迭代器參數來拷貝一個範圍時,就不要求容器類型是相同的了。並且,新容器和原容器中的元素類型也能夠不一樣,只要能將要拷貝的元素轉換爲要初始化的容器的元素類型便可(構造函數只是讀取範圍中的元素並進行拷貝)。

    // 每一個容器有三個元素,用給定的初始化器進行初始化
    list<string> authors = {"Milton", "Shakespeare", "Austen"};
    vector<const char*> articles = {"a", "an", "the"};
    
    list<string> list2(authors); // 正確:類型匹配
    deque<string> authList(authors); // 錯誤:容器類型不匹配
    vector<string> words(articles); // 錯誤:容器類型必須匹配
    // 正確:能夠將const char*元素轉換爲string
    forward_list<string> words(articles.begin(),articles.end());
  2. 若是元素類型是內置類型或者是具備默認構造函數的類類型,能夠只爲構造函數提供一個容器大小參數。若是元素類型沒有默認構造函數,除了大小參數外,還必須指定一個顯示的元素初始值。

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class A
    {
    private:
        vector<int> data;
    
    public:
        A(vector<int>::iterator beg, vector<int>::iterator end)
        {
            data.assign(beg, end);
        }
        void display()
        {
            for (auto i : data)
                cout << i << " ";
            cout << endl;
        }
    };
    
    int main()
    {
        vector<int> iv{1, 2, 3};
        vector<A> one(5, A(iv.begin(), iv.end()));
        for (auto i : one)
            i.display();
        cout << endl;
        return 0;
    }
  3. 只有順序容器的構造函數才接受大小參數,關聯容器並不支持。

  4. 標準庫array的大小也是類型的一部分。當定義一個array時,除了指定元素類型,咱們必須同時指定元素類型和大小。

    array<int, 42> // 類型爲:保存42個int的數組
    array<string, 10> // 類型爲:保存10個string的數組
    
    array<int, 10>::size_type i; // 數組類型包括元素類型和大小
    array<int>::size_type j; // 錯誤:array<int>不是一個類型
  5. 一個默認構造的array是非空的:它包含了於其大小同樣多的元素。這些元素都被默認初始化,就像一個內置數組中的元素那樣。若是咱們對array進行列表初始化,初始值的數目必須等於或小於array的大小。若是初始值數目小於array的大小,則它們被用來初始化array中靠前的元素,全部剩餘元素都會進行值初始化。在這兩種狀況下,若是元素類型是一個類類型,那麼該類必須有一個默認構造函數,以使值初始化可以進行。

    array<int, 10> ia1; // 10個默認初始化的int
    array<int, 10> ia2 = {0,1,2,3,4,5,6,7,8,9}; // 列表初始化
    array<int, 10> ia3 = {42}; // ia3[0]爲42,剩餘元素爲0
    
    int digs[10] = {0,1,2,3,4,5,6,7,8,9};
    int cpy[10] = digs; // 錯誤:內置數組不支持拷貝或賦值
    array<int, 10> digits = {0,1,2,3,4,5,6,7,8,9};
    array<int, 10> copy = digits; // 正確:只要數組類型匹配即合法
  6. 與其餘容器同樣,array也要求初始值的類型必須與要建立的容器類型相同。此外,array還要求元素類型和大小也都同樣,由於大小是array類型的一部分。

  7. 與賦值相關的運算符可用於全部容器。賦值運算符將其左邊容器中的所有元素替換爲右邊容器中元素的拷貝。若是兩個容器原來大小不一樣,賦值運算後二者的大小都與右邊容器的原大小相同。

    c1 = c2; // 將c1的內容替換爲c2中元素的拷貝
    c1 = {a,b,c}; // 賦值後,c1大小爲3
  8. 與內置數組不一樣,標準庫array類型容許賦值。賦值號左右兩邊的運算對象必須具備相同的類型。因爲右邊運算對象的大小可能與左邊運算對象的大小不一樣,所以array類型不支持assign,也不容許用花括號包圍的值列表進行賦值。

    array<int, 10> a1 = {0,1,2,3,4,5,6,7,8,9};
    array<int, 10> a2 = {0}; // 全部元素值均爲0
    a1 = a2; // 替換a1中的元素
    a2 = {0}; // 錯誤:不能將一個花括號列表賦予數組
容器賦值運算 解釋
c1=c2 將c1中的元素替換爲c2中元素的拷貝。c1和c2必須具備相同的類型
c={a,b,c...} 將c1中元素替換爲初始化列表中元素的拷貝(array不適用)
swap(c1,c2) 交換c1和c2中的元素。c1和c2必須具備相同的類型。swap一般比從c2向c1拷貝元素快得多
—— assign操做不適用於關聯容器和array
seq.assign(b,e) 將seq中的元素替換爲迭代器b和e所表示的範圍中的元素。迭代器b和e不能指向seq中的元素
seq.assign(il) 將seq中的元素替換爲初始化列表il(initializer_list)中的元素
seq.assign(n,t) 將seq中的元素替換爲n個值爲t的元素
  1. 賦值相關運算會致使指向左邊容器內部的迭代器、引用和指針失效。而swap操做將容器內容交換不會致使指向容器的迭代器、引用和指針失效(容器類型爲array和string的狀況除外)。

  2. 順序容器的assign成員容許咱們從一個不一樣但相容的類型賦值,或者從容器的一個子序列賦值。assign操做用參數所指定的元素(的拷貝)替換左邊容器中的全部元素。

    list<string> names;
    vector<const char*> oldstyle;
    names = oldstyle; // 錯誤:容器類型不匹配
    // 正確:能夠將const char*轉換爲string
    names.assign(oldstyle.cbegin(), oldstyle.cend());
  3. 因爲其舊元素被替換,所以傳遞給assign的迭代器不能指向調用assign的容器。

  4. 除array外,swap不對任何元素進行拷貝、刪除或插入操做,所以能夠保證在常數時間內完成。

  5. 除string外,指向容器的迭代器、引用和指針在swap操做以後都不會失效。它們仍指向swap操做以前所指向的那些元素。可是,在swap以後,這些元素已經屬於不一樣的容器了。對一個string調用swap會致使迭代器、引用和指針失效。

  6. swap兩個array會真正交換它們的元素。所以,交換兩個array所需的時間與array中元素的數目成正比。

  7. 對於array,在swap操做以後,指針、引用和迭代器所綁定的元素保持不變,但元素值已經與另外一個array中對應元素進行了交換。

  8. 在標準庫中,容器既提供成員函數版本的swap,也提供非成員版本的swap。非成員版本的swap在泛型編程中是很是重要的。統一使用非成員版本的swap是一個好習慣。

  9. 成員函數size返回容器中元素的數目;empty當size爲0時返回布爾值true,不然返回false;max_size返回一個大於或等於該類型容器所能容納的最大元素數的值。forward_list支持max_size和empty,但不支持size。

  10. 每一個容器類型都支持相等運算符(==和!=);除了無序關聯容器都支持關係運算符(>、>=、<、<=)。關聯運算符左右兩邊的運算對象必須是相同類型的容器,且必須保存相同類型的元素。

  11. 比較兩個容器其實是進行元素的逐對比較。這些運算符的工做方式與string的關係運算相似。

    • 若是兩個容器具備相同大小且全部元素都兩兩對應相等,則這兩個容器相等;不然兩個容器不等。
    • 若是兩個容器大小不一樣,但較小容器中每一個元素都等於較大容器中的對應元素,則較小容器小於較大容器。
    • 若是兩個容器都不是另外一個容器的前綴子序列,則它們的比較結果取決於第一個不相等的元素的比較結果。
  12. 只有當其元素類型也定義了相應的比較運算符時,咱們纔可使用關係運算符來比較兩個容器。

  13. 容器的相等運算符其實是使用元素的==運算符實現比較的,而其餘關係運算符是使用元素的<運算符。若是元素類型不支持所需運算符,那麼保存這種元素的容器就不能使用相應的關係運算。

向順序容器添加元素的操做 解釋
—— 這些操做會改變容器的大小;array不支持這些操做
—— forward_list有本身專有版本的insert和emplace
—— forward_list不支持push_back和emplace_back
—— vector和string不支持push_front和emplace_front
c.push_back(t)或c.emplace_back(args) 在c的尾部建立一個值爲t或由args建立的元素。返回void
c.push_front(t)或c.emplace_front(args) 在c的頭部建立一個值爲t或由args建立的元素。返回void
c.insert(p,t)或c.emplace(p,args) 在迭代器p指向的元素以前建立一個值爲t或由args建立的元素。返回指向新添加的元素的迭代器
c.insert(p,n,t) 在迭代器p指向的元素以前插入n個值爲t的元素。返回指向新添加的第一個元素的迭代器;若n爲0,則返回p
c.insert(p,b,e) 將迭代器b和e指定的範圍內的元素插入到迭代器p指向的元素以前。b和e不能指向c中的元素(insert會破壞迭代器)。返回指向新添加的第一個元素的迭代器;若範圍爲空,則返回p
c.insert(p,il) il是一個花括號包圍的元素值列表(initializer_list)。將這些給定值插入到迭代器p指向的元素以前。返回指向新添加的第一個元素的迭代器;若列表爲空,則返回p
  1. 向一個vector、string或deque插入元素會使全部指向容器的迭代器、引用和指針失效。

  2. 不一樣容器使用不一樣的策略來分配元素空間,而這些策略直接影響性能。

  3. 當咱們用一個對象來初始化容器時,或將一個對象插入到容器中時,實際上放入到容器中的是對象的一個拷貝,而不是對象自己。就像咱們將一個對象傳遞給非引用參數同樣,容器中的元素與提供值的對象之間沒有任何關聯。隨後對容器中元素的任何改變都不會影響到原始對象,反之亦然。

  4. 經過使用insert的返回值,能夠在容器中一個特定位置反覆插入元素:

    list<string> lst;
    auto iter = lst.begin();
    while (cin >> word)
    	iter = lst.insert(iter, word); // 等價於調用push_front
  5. emplace_front、emplace和emplace_back這些操做構造而不是拷貝元素。

  6. emplace函數在容器中直接構造元素。傳遞給emplace函數的參數必須與元素類型的構造函數相匹配。

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class A
    {
    private:
        vector<int> data;
    
    public:
        A(vector<int>::iterator beg, vector<int>::iterator end)
        {
            data.assign(beg, end);
        }
        void display()
        {
            for (auto i : data)
                cout << i << " ";
            cout << endl;
        }
    };
    
    int main()
    {
        vector<int> iv{1, 2, 3};
        vector<A> one(5, A(iv.begin(), iv.end()));
        for (auto i : one)
            i.display();
        cout << endl;
        
        vector<int> ivv{2, 3, 6};
        one.emplace_back(ivv.begin(), ivv.end());
        one.back().display();
        return 0;
    }
  7. 包括array在內的每一個順序容器都有一個front成員函數,而除forward_list以外的全部順序容器都有一個back成員函數。這兩個操做分別返回首元素和尾元素的引用:

    // 在解引用一個迭代器或調用front或back以前檢查是否有元素
    if (!c.empty())
    {
    	// val和val2是c中第一個元素值的拷貝
    	auto val = *c.begin(), val2 = c.front();
    	// val3和val4是c中最後一個元素值的拷貝
    	auto last = c.end();
    	auto val3 = *(--last); // forward_list迭代器不能執行--操做
    	auto val4 = c.back(); // forward_list不支持
    }
  8. 在調用front和back(或解引用begin和end返回的迭代器)以前,要確保c非空。若是容器爲空,if中操做的行爲將是未定義的。

在順序容器中訪問元素的操做 解釋
—— at和下標操做只適用於string、vector、deque和array
—— back不適用於forward_list
c.back() 返回c中尾元素的引用。若c爲空,函數行爲未定義。
c.front() 返回c中首元素的引用。若c爲空,函數行爲未定義。
c[n] 返回c中下標爲n的元素的引用,n是一個無符號整數。若n>=c.size(),則函數行爲未定義
c.at(n) 返回下標爲n的元素的引用。相似下標運算符,但若是下標越界,則拋出一out_of_range異常
  1. 對一個空容器調用front和back,就像使用一個越界的下標同樣,是一種嚴重的程序設計錯誤。

  2. 在容器中訪問元素的成員函數(即,front、back、下標和at)返回的都是引用。若是容器是一個const對象,則返回值是const的引用。若是容器不是const的,則返回值是普通引用,咱們能夠用來改變元素的值:

    if (!c.empty())
    {
    	c.front() = 42; // 將42賦予c中的第一個元素
    	auto &v = c.back(); // 得到指向最後一個元素的引用
    	v = 1024; // 改變c中的元素
    	auto v2 = c.back(); // v2不是一個引用,它是c.back()的一個拷貝
    	v2 = 0; // 未改變c中的元素
        // 若是咱們使用auto變量來保存這些函數的返回值,而且但願使用此變量來改變元素的值,必須記得將變量定義爲引用類型
    }
順序容器的刪除操做 解釋
—— 這些操做會改變容器的大小,因此不適用於array
—— forward_list有特殊版本的erase
—— forward_list不支持pop_back;vector和string不支持pop_front
c.pop_back() 刪除c中尾元素。若c爲空,則函數行爲未定義。函數返回void
c.pop_front() 刪除c中首元素。若c爲空,則函數行爲未定義。函數返回void
c.erase(p) 刪除迭代器p所指定的元素,返回一個指向被刪元素以後元素的迭代器,若p指向尾元素,則返回尾後迭代器。若p是尾後迭代器,則函數行爲未定義
c.erase(b,e) 刪除迭代器b和e所指定範圍內的元素。返回一個指向最後一個被刪元素以後元素的迭代器,若e自己就是尾後迭代器,則函數也返回尾後迭代器(總之返回e)
c.clear() 刪除c中的全部元素。返回void
  1. 刪除deque中除首尾位置以外的任何元素都會使全部迭代器、引用和指針失效。指向vector或string中刪除點以後位置的迭代器、引用和指針都會失效。
  2. 刪除元素的成員函數並不檢查其參數。在刪除元素以前,程序員必須確保它(們)是存在的。
  3. 在一個單向鏈表中,沒有簡單的辦法來獲取一個元素的前驅。所以,在一個forward_list中添加或刪除元素的操做是經過改變給定元素以後的元素來完成的。爲了支持這些操做,forward_list也定義了before_begin,它返回一個首前迭代器(容許咱們添加刪除鏈表首元素)
在forward_list中插入或刪除元素的操做 解釋
lst.before_begin()或lst.cbefore_begin() 返回指向鏈表首元素以前不存在的元素的迭代器。此迭代器不能解引用。cbefore_begin()返回一個const_iterator
lst.insert_after(p,t)或lst.insert_after(p,n,t)或lst.insert_after(p,b,e)或lst.insert_after(p,il) 在迭代器p以後的位置插入元素。t是一個對象,n是數量,b和e是表示範圍的一對迭代器(b和e不能指向lst內),il是一個花括號列表(initializer_list)。返回一個指向最後一個插入元素的迭代器。若是範圍爲空,則返回p。若p爲尾後迭代器,則函數行爲未定義
emplace_after(p,args) 使用args在p指定的位置以後建立一個元素。返回一個指向這個新元素的迭代器。若p爲尾後迭代器,則函數行爲未定義
lst.erase_after(p)或lst.erase_after(b,e) 刪除p指向的位置以後的元素,或刪除從b以後直到(但不包含)e之間的元素。返回一個指向被刪元素以後元素的迭代器,若不存在這樣的元素,則返回尾後迭代器。若是p指向lst的尾元素或者是一個尾後迭代器,則函數行爲未定義
// 示例代碼:
forward_list<int> flst = {0,1,2,3,4,5,6,7,8,9};
auto prev = flst.before_begin(); // 表示flst的「首前元素」
auto curr = flst.begin(); // 表示flst中的第一個元素
while (curr != flst.end()) // 仍有元素要處理
{
	if (*curr % 2) // 若元素爲奇數,也能夠用*curr & 1
		curr = flst.erase_after(prev); // 刪除它並移動curr
	else
	{
		prev = curr; // 移動迭代器curr,指向下一個元素,prev指向
		++curr; // curr以前的元素
	}
}
順序容器大小操做 解釋
—— resize不適用於array
c.resize(n) 調整c的大小爲n個元素。若n<c.size(),則多出的元素被丟棄。若必須添加新元素(n>c.size()),對新元素進行值初始化。
c.resize(n,t) 調整c的大小爲n個元素。任何新添加的元素都初始化爲值t
  1. 若是resize縮小容器,則指向被刪除元素的迭代器、引用和指針都會失效;對vector、string或deque進行resize可能致使迭代器、指針和引用失效。

  2. 容器操做可能使迭代器失效。使用失效的迭代器、指針或引用是嚴重的運行時錯誤。

    • 在向容器添加元素後:
      • 若是容器是vector或string,且存儲空間被從新分配,則指向容器的迭代器、指針和引用都會失效。若是存儲空間未從新分配,指向插入位置以前的元素的迭代器、指針和引用仍有效,但指向插入位置以後元素的迭代器、指針和引用將會失效。
      • 對於deque,插入到除首尾位置以外的任何位置都會致使迭代器、指針和引用失效。若是在首尾位置添加元素,迭代器會失效,但指向存在的元素的引用和指針不會失效。
      • 對於list和forward_list,指向容器的迭代器(包括尾後迭代器和首前迭代器)、指針和引用仍有效。
    • 當咱們從一個容器中刪除元素後,指向被刪除元素的迭代器、指針和引用必定會失效,畢竟,這些元素都已經被銷燬了。當咱們刪除一個元素後:
      • 對於list和forward_list,指向容器其餘位置的迭代器(包括尾後迭代器和首前迭代器)、引用和指針仍有效。
      • 對於deque,若是在首尾以外的任何位置刪除元素,那麼指向被刪除元素外其餘元素的迭代器、引用和指針也會失效。若是是刪除deque的尾元素,則尾後迭代器也會失效,但其餘迭代器、引用和指針不受影響;若是是刪除首元素,這些也不會受影響。
      • 對於vector和string,指向被刪元素以前元素的迭代器、引用和指針仍有效。注意:當咱們刪除元素時,尾後迭代器老是會失效。
  3. 當你使用迭代器(或指向容器元素的引用或指針)時,最小化要求迭代器必須保持有效的程序片斷是一個好的辦法。因爲向迭代器添加元素和從迭代器刪除元素的代碼可能會使迭代器失效,所以必須保證每次改變容器的操做以後都正確地從新定位迭代器。這個建議對vector、string和deque尤其重要。

  4. 程序必須保證每一個循環布中都更新迭代器、引用和指針:

    // 傻瓜循環,刪除偶數元素,複製每一個奇數元素
    vector<int> vi = {0,1,2,3,4,5,6,7,8,9};
    auto iter = vi.begin(); // 調用begin而不是cbegin,由於咱們要改變vi
    while (iter != vi.end())
    {
    	if (*iter % 2)
    	{
    		iter = vi.insert(iter, *iter); // 複製當前元素
    		iter += 2; // 向前移動迭代器,跳過插入到它以前的元素以及當前元素
    	}
    	else
    	{
    		iter = vi.erase(iter); // 刪除偶數元素
    		// 不該向前移動迭代器,iter指向咱們刪除的元素以後的元素
    	}
    }
  5. 不要保存end返回的迭代器。當咱們添加、刪除vector或string的元素後,或在deque中首元素以外任何位置添加、刪除元素後,原來end返回的迭代器老是會失效。所以,添加或刪除元素的循環程序必須反覆調用end,而不能在循環以前保存end返回的迭代器,一直看成容器末尾使用。

容器大小管理操做 解釋
—— shrink_to_fit只適用於vector、string和deque
—— capacity和reserve只適用於vector和string
c.shrink_to_fit() 請將capacity()減小爲與size()相同大小(具體的實現能夠選擇忽略此請求,即,調用shrink_to_fit並不保證必定退回內存空間)
c.capacity() 不從新分配內存空間的話,c能夠保存多少元素
c.reserve(n) 分配至少能容納n個元素的內存空間
  1. reserve並不改變容器中元素的數量,它僅影響vector預先分配多大的內存空間。只有當須要的內存空間超過當前容量時,reserve調用纔會改變vector的容量,若是需求大小大於當前容量,reserve至少分配與需求同樣大的內存空間(可能更大)。若是需求大小小於或等於當前容量,reserve什麼也不作。特別是,當需求大小小於當前容量時,容器不會退回內存空間。所以,在調用reserve以後,capacity將會大於或等於傳遞給reserve的參數。這樣,調用reserve永遠也不會減小容器佔用的內存空間。相似的,resize成員函數只能改變容器中元素的數目,而不是容器的容量。咱們一樣不能使用resize來減小容器預留的內存空間。

  2. 每一個vector實現均可以選擇本身的內存分配策略。可是必須遵照的一條原則是:只有當無可奈何(在執行insert操做時size與capacity相等,或者調用resize或reserve時給定的大小超過當前capacity)時才能夠分配新的內存空間。

構造string的其餘方法 解釋
—— n、len2和pos2都是無符號值
string s(cp,n) s是cp指向的數組中前n個字符的拷貝。此數組至少應該包含n個字符
string s(s2,pos2) s是string s2從下標pos2開始的字符的拷貝。若pos2>s2.size(),構造函數的行爲未定義
string s(s2,pos2,len2) s是string s2從下標pos2開始len2個字符的拷貝。若pos2>s2.size(),構造函數的行爲未定義。無論len2的值是多少,構造函數至多拷貝s2.size()-pos2個字符。
子字符串操做 解釋
s.substr(pos,n) 返回一個string,包含s中從pos開始的n個字符的拷貝。pos的默認值爲0.n的默認值爲s.size()-pos,即拷貝從pos開始的全部字符
  1. 在c++11中,vector 增長了data()的用法,它返回內置vecotr所指的數組內存的第一個元素的指針。
修改string的操做 解釋
s.insert(pos,args) 在pos以前插入args指定的字符。pos能夠是一個下標或一個迭代器。接受下標的版本返回一個指向s的引用;接受迭代器的版本返回指向第一個插入字符的迭代器
s.erase(pos,len) 刪除從位置pos開始的len個字符。若是len被省略,則刪除從pos開始直至s末尾的全部字符。返回一個指向s的引用
s.assign(args) 將s中的字符替換爲args指定的字符。返回一個指向s的引用
s.append(args) args追加到s。返回一個指向s的引用
s.replace(range,args) 刪除s中範圍range內的字符,替換爲args指定的字符。range或者是一個下標和一個長度,或者是一對指向s的迭代器。返回一個指向s的引用
—— args能夠是下列形式之一;append和assign可使用全部形式
—— str不能與s相同,迭代器b和e不能指向s
str 字符串str
str,pos,len str中從pos開始最多len個字
cp,len 從cp(char pointer)指向的字符數組的前(最多)len個字符
n,c n個字符c
b,e 迭代器b和e指定的範圍內的字符
初始化列表 花括號包圍的,以逗號分隔的字符列表
—— replace和insert所容許的args形式依賴於rangepos是如何指定的
replace(pos,len,args) replace(b,e,args) insert(pos,args) insert(iter,args) args能夠是
str
str,pos,len
cp,len
cp
n,c
b2,e2
初始化列表
  1. string搜索函數返回string::size_type值,該類型是一個unsigned類型。所以,用一個int或其餘帶符號類型來保存這些函數的返回值不是一個好主意。
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中位置pos開始查找字符串s2。pos默認爲0
cp,pos 從s中位置pos開始查找指針cp指向的以空字符結尾的C風格字符串
cp,pos,n 從s中位置pos開始查找指針cp指向的數組的前n個字符。pos和n無默認值
  1. 指定在哪裏開始搜索:

    string numbers("0123456789"), name("r2d2");
    string::size_type pos = 0;
    // 每步循環查找name中下一個數
    while ((pos = name.find_first_of(numbers, pos)) != string::npos)
    {
    	cout << "found number at index: " << pos
    		<< " element is " << name[pos] << endl;
    	++pos; // 移動到下一個字符
    }
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) 一組重載函數,返回數值val的string表示。val能夠是任何算術類型。對每一個浮點類型和int或更大的整型,都有相應版本的to_string。與往常同樣,小整型會被提高
stoi(s,p,b)或stol(s,p,b)或stoul(s,p,b)或stoll(s,p,b)或stoull(s,p,b) 返回s的起始子串(表示整數內容)的數值,返回值類型分別是int、long、unsigned long、long long、unsigned long long。b表示轉換所用的基數,默認值爲10。p是size_t指針,用來保存s中第一個非數值字符的下標,p默認爲0,即,函數不保存下標
stof(s,p)或stod(s,p)或stold(s,p) 返回s的起始子串(表示浮點數內容)的數值,返回值類型分別是float、double、或long double。參數p的做用與整數轉換函數中同樣
—— string參數中第一個非空白符必須是符號(+或-)或數字。它能夠以0x或0X開頭來表示十六進制數。對那些將字符串轉換爲浮點值的函數,string參數也能夠以小數點(.)開頭,並能夠包含e或E來表示指數部分。對於那些將字符串轉換爲整型值的函數,根據基數不一樣,string參數能夠包含字母字符,對應大於數字9的數。
—— 若是string不能轉換爲一個數值,這些函數拋出一個invalid_argument異常。若是轉換獲得的數值沒法用任何類型來表示,則拋出一個out_of_range異常。
  1. 適配器是標準庫中的一個通用概念。本質上,一個適配器是一種機制,能使某種事物的行爲看起來像另一種事物同樣。一個容器適配器接受一種已有的容器類型,是其行爲看起來像一種不一樣的類型。
全部容器適配器都支持的操做和類型 解釋
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的內容,a和b必須有相同類型,包括底層容器類型也必須相同
  1. 默認狀況下,stack和queue是基於deque實現的,priority_queue是在vector之上實現的。咱們能夠在建立一個適配器時將一個命名的順序容器做爲第二個類型參數,來重載默認容器類型。

    // 從deq拷貝元素到stk
    stack<int> stk(deq);
    // 在vector上實現的空棧
    stack<string, vector<string>> str_stk;
    // str_stk2在vector上實現,初始化時保存svec的拷貝
    stack<string, vector<string>> str_stk2(svec); // svec是vector<string>類型
額外的棧操做 解釋
—— 棧默認基於deque實現,也能夠在list或vector之上實現。
s.pop() 刪除棧頂元素,但不返回該元素值
s.push(item) 建立一個新元素壓入棧頂,該元素經過拷貝或移動item而來,或者由args構造
s.top() 返回棧頂元素,但不將元素彈出棧
額外的queue和priority_queue操做 解釋
—— queue默認基於deque實現,priority_queue默認基於vector實現
—— queue也能夠用list或vector實現,priority_queue也能夠用deque實現
q.pop() 彈出queue的首元素或priority_queue的最高優先級的元素,但不返回此元素
q.front() 返回首元素或尾元素,但不刪除此元素
q.back() 只適用於queue
q.top() 返回最高優先級元素,但不刪除該元素
q.push(item)或q.emplace(args) 在queue末尾或priority_queue中恰當的位置建立一個元素,其值爲item,或者由args構造
  1. deque支持在容器頭尾位置的快速插入和刪除,並且在兩端插入或刪除元素都不會致使從新分配空間
相關文章
相關標籤/搜索