1. 程式人生 > >C++ STL(一)

C++ STL(一)

一、STL基本概念

容器:可容納各種資料型別的通用資料結構

迭代器:用於依次存取容器中元素。

演算法:操作容器中元素的函式模板。

 

二、容器

1.容器概述

順序容器:vector,deque,list

關聯容器:set,multiset,map,multimap

容器介面卡:stack,queue,priority_queue

 

2.順序容器

特點:容器並非排序,元素的插入位置與元素的值無關。

  • vector:一種隨機訪問的陣列型別,提供了對陣列元素進行快速隨機訪問以及在序列尾部進行快速的插入和刪除操作的功能。可以再需要的時候修改其自身的大小,可以認為是動態陣列。支援迭代器類別:隨機訪問
  • deque:一種隨機訪問的陣列型別,提供了序列兩端快速進行插入和刪除操作的功能。可以再需要的時候修改其自身的大小,可以認為是雙向佇列。支援迭代器類別:隨機訪問
  • list:一種不支援隨機訪問的陣列型別,插入和刪除所花費的時間是固定的,與位置無關。可認為是雙向連結串列。支援迭代器類別:雙向

順序容器常用的成員函式:

front:返回容器中第一個元素的引用

back:返回容器中最後一個元素的引用

push_back:在容器末尾增加新元素

pop_back:刪除容器末尾的元素

erase:刪除迭代器指向的元素,或刪除一個區間,返回被刪除元素後面的那個元素的迭代器。

 

3.關聯容器

特點:元素始終是排序的,通常都是以平衡二叉樹實現,在查詢時具有較好的效能。

  • set:一種隨機存取的容器,其關鍵字和資料元素是同一個值。所有元素都必須具有惟一值。支援迭代器類別:雙向
  • multiset:一種隨機存取的容器,其關鍵字和資料元素是同一個值。可以包含重複的元素。支援迭代器類別:雙向
  • map:一種包含成對數值的容器,一個值是實際資料值,另一個是用來尋找資料的關鍵字。一個特定的關鍵字只能與一個元素關聯。支援迭代器類別:雙向
  • multimap:一種包含成對數值的容器,一個值是實際資料值,另一個是用來尋找資料的關鍵字。一個關鍵字可以與多個數據元素關聯。支援迭代器類別:雙向

關聯容器常用成員函式(順序容器也有)

begin:返回指向容器中第一個元素的迭代器

end:返回指向容器中最後一個元素後面位置的迭代器

rbegin:返回指向容器中最後一個元素的迭代器

rend:返回指向容器中第一個元素前面位置的迭代器

erase:從容器中刪除一個或幾個元素

clear:從容器中刪除所有元素

 

4.容器介面卡(不支援迭代器)

  • stack:介面卡容器型別,用vector,deque或list物件建立了一個先進後出容器,可認為是棧
  • queue:介面卡容器型別,用deque或list物件建立了一個先進先出容器,可認為是佇列
  • priority_queue:介面卡容器型別,用vector或deque物件建立了一個排序佇列,可認為是優先順序佇列

 

三、迭代器

1.概述

  • 用於指向順序容器和關聯容器中的元素
  • 可以通過迭代器讀取指向的元素
  • 迭代器有const和非const,對於非const迭代器能修改其指向的元素

 

2.迭代器類別

迭代器類別

說明

輸入

從容器中讀取元素。輸入迭代器只能一次讀入一個元素向前移動,

輸入迭代器只支援一遍演算法,同一個輸入迭代器不能兩遍遍歷一個序列

輸出

向容器中寫入元素。輸出迭代器只能一次一個元素向前移動。

輸出迭代器只支援一遍演算法,統一輸出迭代器不能兩次遍歷一個序列

正向

組合輸入迭代器和輸出迭代器的功能,並保留在容器中的位置

雙向

組合正向迭代器和逆向迭代器的功能,支援多遍演算法

隨機訪問

組合雙向迭代器的功能與直接訪問容器中任何元素的功能,即可向前向後跳過任意個元素

3.迭代器操作

每種迭代器均可進行包括表中前一種迭代器可進行的操作。

迭代器操作

說明

所有迭代器

p++

後置自增迭代器

++p

前置自增迭代器

輸入迭代器

*p

復引用迭代器,作為右值

p=p1

將一個迭代器賦給另一個迭代器

p==p1

比較迭代器的相等性

p!=p1

比較迭代器的不等性

輸出迭代器

*p

復引用迭代器,作為左值

p=p1

將一個迭代器賦給另一個迭代器

正向迭代器

提供輸入輸出迭代器的所有功能

雙向迭代器

--p

前置自減迭代器

p--

後置自減迭代器

隨機迭代器

p+=i

將迭代器遞增i位

p-=i

將迭代器遞減i位

p+i

在p位加i位後的迭代器

p-i

在p位減i位後的迭代器

p

返回p位元素偏離i位的元素引用

p<p1

如果迭代器p的位置在p1前,返回true,否則返回false

p<=p1

p的位置在p1的前面或同一位置時返回true,否則返回false

p>p1

如果迭代器p的位置在p1後,返回true,否則返回false

p>=p1

p的位置在p1的後面或同一位置時返回true,否則返回false

 

4.示例

使用迭代器

#include <vector>
#include <iostream>
using namespace std;

int main()
{
    // 定義了一個存放int型別元素的陣列
    vector<int> v;

    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    // 定義const迭代器
    vector<int>::const_iterator i;
    for(i = v.begin(); i != v.end(); ++i)
        cout << *i << " ";
    cout << endl;

    // 定義反向迭代器
    vector<int>::reverse_iterator r;
    for(r = v.rbegin(); r != v.rend(); ++r)
        cout << *r << " ";
    cout << endl;

    // 定義非const型別迭代器
    vector<int>::iterator j;
    for(j = v.begin(); j != v.end(); ++j)
        *j = 100;
    for(i = v.begin(); i != v.end(); ++i)
        cout << *i << " ";

    /*
    輸出:
    1 2 3 4
    4 3 2 1
    100 100 100 100
    */
}

遍歷vector的方法有多種,deque也是這樣:

#include <vector>
#include <iostream>

using namespace std;

int main()
{
    vector<int> v(100);

    // 1
    for(unsigned int i = 0; i < v.size(); ++i)
        cout << v[i] << " ";
    // 2
    vector<int>::const_iterator ii;
    for(ii = v.begin(); ii != v.end(); ++ii)
        cout << *ii << " ";
    // 3
    for(ii = v.begin(); ii < v.end();)
    {
        cout << *ii << " ";
        ii = ii + 2;
    }
}

但在遍歷list需要注意不能使用隨機訪問的方法

#include <list>
#include <iostream>

using namespace std;

int main()
{
    list<int> v(10);
    list<int>::const_iterator ii;
    for(ii = v.begin(); ii != v.end(); ++ii)
        cout << *ii << " ";
}

注意之前上個示例所示的另外兩種方法在此都不能使用。

 

四、演算法

1.概述

  • 演算法即函式模板
  • 演算法通過迭代器操縱容器中的元素
  • 演算法可以處理容器,也可以處理普通陣列

2.演算法示例

#include <list>
#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

int main()
{
    int a[10] = {1,2,3,4};
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    vector<int>::iterator p;
    p = find(v.begin(), v.end(), 89);
    if(p != v.end())
        cout << *p << endl;
    else
        cout << "not found" << endl;

    // 整個容器:[1,2,3,4], 查詢區間: [2,3)
    p = find(v.begin() + 1, v.end() - 2, 1);

    // 陣列名是迭代器
    int * pp = find(a, a + 4, 4);
    cout << *pp << endl;
}

 

五、函式物件

#include <iostream>

using namespace std;

class CmyAverage{
public:
    double operator() (int a1, int a2, int a3)
    {
        return (a1 + a2 + a3) / 3.0;
    }
};
int main()
{
    CmyAverage average;
    cout << average(1,3,3);
    return 0;
}

 

1.Accumulate函式物件

例如accumulate的函式頭

template<class InIt, class T, class Pred>
T accumulate(InIt first, InIt last, T val, Pred pr)

這裡的pr就是函式物件,通過的[first,last)中的每個迭代器執行val = pr(val, *I),返回最終結果。

 

以Accumulate的原始碼為例,accumulate有兩個不一樣的版本,一個是不帶下面所示的_BinaryOperation引數,這相當於使用預設形式下的accumulate,本文所給的是通過自定義函式進行accumulate。

template<typename _InputIterator, typename _Tp,
            typename _BinaryOperation>
    _Tp accumulate(_InputIterator _first, _InputIterator _last,
                   _Tp _init,   _BinaryOperation _binary_op)
    {
        for(; _first != _last; ++_first)
            _init = _binary_op(_init, *_first);
        return _init;
    }

結合上面Accumulate的原始碼

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>

using namespace std;

int SumSquares(int total, int value)
{ return total + value * value; }

template <class T>
void PrintInterval(T first, T last)
{
    for(; first != last; ++first)
        cout << *first << " ";
    cout << endl;
}

template <class T>
class SumPowers{
private:
    int power;
public:
    SumPowers(int p):power(p){ }
    const T operator() (const T & total, const T & value)
    {
        T v = value;
        for(int i = 0; i < power - 1; ++i)
            v = v * value;
        return total + v;
    }
};

int main()
{
    const int SIZE = 10;
    int a1[] = { 1,2,3,4,5,6,7,8,9,10 };
    vector<int> v(a1,a1+SIZE);
    cout << "1) ";
    PrintInterval(v.begin(),v.end());
    int result = accumulate(v.begin(),v.end(),0,SumSquares);
    cout << "2) 平方和:" << result << endl;
    result = accumulate(v.begin(),v.end(),0,SumPowers<int>(3));
    cout << "3) 立方和:" << result << endl;
    result = accumulate(v.begin(),v.end(),0,SumPowers<int>(4));
    cout << "4) 4次方和:" << result;
    return 0;
}

 

2.greater函式物件

template<class T>
struct greater : public binary_function<T, T, bool>
{
    bool operator() (const T & x, const T & y) const
    {
        return x > y;
    }
};

template<class Arg1, class Arg2, class Result>
struct binary_function{
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

greater函式模板生成函式物件。這裡自定義函式物件MyLess

#include <iostream>
#include <list>
#include <iterator>

using namespace std;

class MyLess{
public:
    bool operator() (const int & c1, const int & c2)
    {
        return (c1 % 10) < (c2 % 10);
    }
};

int main()
{
    const int size = 5;
    int a[size] = {5, 21, 14, 2, 3};
    list<int> lst(a, a + size);
    lst.sort(MyLess());
    ostream_iterator<int> output(cout, ",");
    copy(lst.begin(), lst.end(), output);
    cout << endl;
    // 進行降序排序
    lst.sort(greater<int>());

    copy(lst.begin(), lst.end(), output);
    cout << endl;
}

一個練習就是寫出下面的MyMax函式

#include <iostream>
#include <list>
#include <iterator>

using namespace std;

class MyLess{
public:
    bool operator() (int a1, int a2)
    {
        if((a1 % 10) < (a2 % 10))
            return true;
        else
            return false;
    }
};

bool MyCompare(int a1, int a2)
{
    if((a1 % 10) < (a2 % 10))
        return false;
    else
        return true;
}

template <class T, class Pred>
T MyMax(T * a, int len , Pred fun)
{
    T temp = a[0];
    for(int i = 1; i < len; ++i)
    {
        if(fun(temp, a[i]))
            temp = a[i];
    }
    return temp;
}

int main()
{
    int a[] = {35, 7, 13, 19, 12};
    cout << MyMax(a, 5, MyLess()) << endl;
    cout << MyMax(a, 5, MyCompare) << endl;
    return 0;
}

 

參考:

《C++ Primer》第五版

郭煒老師的《C++面向物件程式設計》