STL提供了大量的模板類和函數,可以在OOP和常規編程中使用。所有的STL的大約50個算法都是完全通用的,而且不依賴於任何特定的數據類型。下面的小節說明了三個基本的STL組件:
1)迭代器提供了訪問容器中對象的方法。例如,可以使用一對迭代器指定list或vector中的一定范圍的對象。迭代器就如同一個指針。事實上,C++的指針也是一種迭代器。但是,迭代器也可以是那些定義了operator*()以及其他類似於指針的操作符地方法的類對象。
2)容器是一種數據結構,如list,vector,和deques ,以模板類的方法提供。為了訪問容器中的數據,可以使用由容器類輸出的迭代器。
3)算法是用來操作容器中的數據的模板函數。例如,STL用sort()來對一個vector中的數據進行排序,用find()來搜索一個list中的對象。函數本身與他們操作的數據的結構和類型無關,因此他們可以在從簡單數組到高度復雜容器的任何數據結構上使用。
函數和函數對象
STL中,函數被稱為算法,也就是說它們和標准C庫函數相比,它們更為通用。STL算法通過重載operator()函數實現為模板類或模板函數。這些類用於創建函數對象,對容器中的數據進行各種各樣的操作。下面的幾節解釋如何使用函數和函數對象。
函數和斷言
經常需要對容器中的數據進行用戶自定義的操作。例如,你可能希望遍歷一個容器中所有對象的STL算法能夠回調自己的函數。例如
- #include <iostream.h>
- #include <stdlib.h> // Need random(), srandom()
- #include <time.h> // Need time()
- #include <vector> // Need vector
- #include <algorithm> // Need for_each()
- #define VSIZE 24 // Size of vector
- vector<long> v(VSIZE); // Vector object
- // Function prototypes
- void initialize(long &ri);
- void show(const long &ri);
- bool isMinus(const long &ri); // Predicate function
- int main()
- {
- srandom( time(NULL) ); // Seed random generator
- for_each(v.begin(), v.end(), initialize);//調用普通函數
- cout << "Vector of signed long integers" << endl;
- for_each(v.begin(), v.end(), show);
- cout << endl;
- // Use predicate function to count negative values
- //
- int count = 0;
- vector<long>::iterator p;
- p = find_if(v.begin(), v.end(), isMinus);//調用斷言函數
- while (p != v.end()) {
- count++;
- p = find_if(p + 1, v.end(), isMinus);
- }
- cout << "Number of values: " << VSIZE << endl;
- cout << "Negative values : " << count << endl;
- return 0;
- }
- // Set ri to a signed integer value
- void initialize(long &ri)
- {
- ri = ( random() - (RAND_MAX / 2) );
- // ri = random();
- }
- // Display value of ri
- void show(const long &ri)
- {
- cout << ri << " ";
- }
- // Returns true if ri is less than 0
- bool isMinus(const long &ri)
- {
- return (ri < 0);
- }
所謂斷言函數,就是返回bool值的函數。
函數對象
除了給STL算法傳遞一個回調函數,你還可能需要傳遞一個類對象以便執行更復雜的操作。這樣的一個對象就叫做函數對象。實際上函數對象就是一個類,但它和回調函數一樣可以被回調。例如,在函數對象每次被for_each()或find_if()函數調用時可以保留統計信息。函數對象是通過重載 operator()()實現的。如果TanyClass定義了opeator()(),那麼就可以這麼使用:
- TAnyClass object; // Construct object
- object(); // Calls TAnyClass::operator()() function
- for_each(v.begin(), v.end(), object);
STL定義了幾個函數對象。由於它們是模板,所以能夠用於任何類型,包括C/C++固有的數據類型,如long。有些函數對象從名字中就可以看出它的用途,如plus()和multiplies()。類似的greater()和less-equal()用於比較兩個值。
注意
有些版本的ANSI C++定義了times()函數對象,而GNU C++把它命名為multiplies()。使用時必須包含頭文件<functional>。
一個有用的函數對象的應用是accumulate() 算法。該函數計算容器中所有值的總和。記住這樣的值不一定是簡單的類型,通過重載operator+(),也可以是類對象。
Listing 8. accum.cpp
- #include <iostream.h>
- #include <numeric> // Need accumulate()
- #include <vector> // Need vector
- #include <functional> // Need multiplies() (or times())
- #define MAX 10
- vector<long> v(MAX); // Vector object
- int main()
- {
- // Fill vector using conventional loop
- //
- for (int i = 0; i < MAX; i++)
- v[i] = i + 1;
- // Accumulate the sum of contained values
- //
- long sum =
- accumulate(v.begin(), v.end(), 0);
- cout << "Sum of values == " << sum << endl;
- // Accumulate the product of contained values
- //
- long product =
- accumulate(v.begin(), v.end(), 1, multiplies<long>());//注意這行
- cout << "Product of values == " << product << endl;
- return 0;
- }
編譯輸出如下:
- $ g++ accum.cpp
- $ ./a.out
- Sum of values == 55
- Product of values == 3628800
『注意使用了函數對象的accumulate()的用法。accumulate() 在內部將每個容器中的對象和第三個參數作為multiplies函數對象的參數,multiplies(1,v)計算乘積。VC中的這些模板的源代碼如下:
- // TEMPLATE FUNCTION accumulate
- template<class _II, class _Ty> inline
- _Ty accumulate(_II _F, _II _L, _Ty _V)
- {for (; _F != _L; ++_F)
- _V = _V + *_F;
- return (_V); }
- // TEMPLATE FUNCTION accumulate WITH BINOP
- template<class _II, class _Ty, class _Bop> inline
- _Ty accumulate(_II _F, _II _L, _Ty _V, _Bop _B)
- {for (; _F != _L; ++_F)
- _V = _B(_V, *_F);
- return (_V); }
- // TEMPLATE STRUCT binary_function
- template<class _A1, class _A2, class _R>
- struct binary_function {
- typedef _A1 first_argument_type;
- typedef _A2 second_argument_type;
- typedef _R result_type;
- };
- // TEMPLATE STRUCT multiplies
- template<class _Ty>
- struct multiplies : binary_function<_Ty, _Ty, _Ty> {
- _Ty operator()(const _Ty& _X, const _Ty& _Y) const
- {return (_X * _Y); }
- };
引言:如果你想深入了解STL到底是怎麼實現的,最好的辦法是寫個簡單的程序,將程序中涉及到的模板源碼給copy下來,稍作整理,就能看懂了。所以沒有必要去買什麼《STL源碼剖析》之類的書籍,那些書可能反而浪費時間。』
1)發生器函數對象
有一類有用的函數對象是“發生器”(generator)。這類函數有自己的內存,也就是說它能夠從先前的調用中記住一個值。例如隨機數發生器函數。
普通的C程序員使用靜態或全局變量 “記憶”上次調用的結果。但這樣做的缺點是該函數無法和它的數據相分離『還有個缺點是要用TLS才能線程安全』。顯然,使用類來封裝一塊:“內存”更安全可靠。先看一下例子:
Listing 9. randfunc.cpp
- #include <iostream.h>
- #include <stdlib.h> // Need random(), srandom()
- #include <time.h> // Need time()
- #include <algorithm> // Need random_shuffle()
- #include <vector> // Need vector
- #include <functional> // Need ptr_fun()
- using namespace std;
- // Data to randomize
- int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- vector<int> v(iarray, iarray + 10);
- // Function prototypes
- void Display(vector<int>& vr, const char *s);
- unsigned int RandInt(const unsigned int n);
- int main()
- {
- srandom( time(NULL) ); // Seed random generator
- Display(v, "Before shuffle:");
- pinter_to_unary_function<unsigned int, unsigned int>
- ptr_RandInt = ptr_fun(RandInt); // Pointer to RandInt()//注意這行
- random_shuffle(v.begin(), v.end(), ptr_RandInt);
- Display(v, "After shuffle:");
- return 0;
- }
- // Display contents of vector vr
- void Display(vector<int>& vr, const char *s)
- {
- cout << endl << s << endl;
- copy(vr.begin(), vr.end(), ostream_iterator<int>(cout, " "));
- cout << endl;
- }
- // Return next random value in sequence modulo n
- unsigned int RandInt(const unsigned int n)
- {
- return random() % n;
- }
編譯運行結果如下:
- $ g++ randfunc.cpp
- $ ./a.out
- Before shuffle:
- 1 2 3 4 5 6 7 8 9 10
- After shuffle:
- 6 7 2 8 3 5 10 1 9 4
首先用下面的語句申明一個對象:
- pointer_to_unary_function<unsigned int, unsigned int>
- ptr_RandInt = ptr_fun(RandInt);
這兒使用STL的單目函數模板定義了一個變量ptr_RandInt,並將地址初始化到我們的函數RandInt()。單目函數接受一個參數,並返回一個值。現在random_shuffle()可以如下調用:
- random_shuffle(v.begin(), v.end(), ptr_RandInt);
在本例子中,發生器只是簡單的調用rand()函數。
關於常量引用的一點小麻煩不翻譯了,VC下將例子中的const去掉)