程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> STL組件之算法(1)

STL組件之算法(1)

編輯:C++入門知識

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算法能夠回調自己的函數。例如

  1. #include <iostream.h>  
  2. #include <stdlib.h> // Need random(), srandom()  
  3. #include <time.h> // Need time()  
  4. #include <vector> // Need vector  
  5. #include <algorithm> // Need for_each()  
  6.  
  7. #define VSIZE 24 // Size of vector  
  8. vector<long> v(VSIZE); // Vector object  
  9.  
  10. // Function prototypes  
  11. void initialize(long &ri);  
  12. void show(const long &ri);  
  13. bool isMinus(const long &ri); // Predicate function  
  14.  
  15. int main()  
  16. {  
  17. srandom( time(NULL) ); // Seed random generator  
  18.  
  19. for_each(v.begin(), v.end(), initialize);//調用普通函數  
  20. cout << "Vector of signed long integers" << endl;  
  21. for_each(v.begin(), v.end(), show);  
  22. cout << endl;  
  23.  
  24. // Use predicate function to count negative values  
  25. //  
  26. int count = 0;  
  27. vector<long>::iterator p;  
  28. p = find_if(v.begin(), v.end(), isMinus);//調用斷言函數  
  29. while (p != v.end()) {  
  30. count++;  
  31. p = find_if(p + 1, v.end(), isMinus);  
  32. }  
  33. cout << "Number of values: " << VSIZE << endl;  
  34. cout << "Negative values : " << count << endl;  
  35.  
  36. return 0;  
  37. }  
  38.  
  39. // Set ri to a signed integer value  
  40. void initialize(long &ri)  
  41. {  
  42. ri = ( random() - (RAND_MAX / 2) );  
  43. // ri = random();  
  44. }  
  45.  
  46. // Display value of ri  
  47. void show(const long &ri)  
  48. {  
  49. cout << ri << " ";  
  50. }  
  51.  
  52. // Returns true if ri is less than 0  
  53. bool isMinus(const long &ri)  
  54. {  
  55. return (ri < 0);  

所謂斷言函數,就是返回bool值的函數。

函數對象

除了給STL算法傳遞一個回調函數,你還可能需要傳遞一個類對象以便執行更復雜的操作。這樣的一個對象就叫做函數對象。實際上函數對象就是一個類,但它和回調函數一樣可以被回調。例如,在函數對象每次被for_each()或find_if()函數調用時可以保留統計信息。函數對象是通過重載 operator()()實現的。如果TanyClass定義了opeator()(),那麼就可以這麼使用:

  1. TAnyClass object; // Construct object  
  2. object(); // Calls TAnyClass::operator()() function  
  3. 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

  1. #include <iostream.h>  
  2. #include <numeric> // Need accumulate()  
  3. #include <vector> // Need vector  
  4. #include <functional> // Need multiplies() (or times())  
  5. #define MAX 10  
  6. vector<long> v(MAX); // Vector object  
  7. int main()  
  8. {  
  9. // Fill vector using conventional loop  
  10. //  
  11. for (int i = 0; i < MAX; i++)  
  12. v[i] = i + 1;  
  13. // Accumulate the sum of contained values  
  14. //  
  15. long sum =  
  16. accumulate(v.begin(), v.end(), 0);  
  17. cout << "Sum of values == " << sum << endl;  
  18. // Accumulate the product of contained values  
  19. //  
  20. long product =  
  21. accumulate(v.begin(), v.end(), 1, multiplies<long>());//注意這行  
  22. cout << "Product of values == " << product << endl;  
  23. return 0;  

編譯輸出如下:

  1. $ g++ accum.cpp  
  2. $ ./a.out  
  3. Sum of values == 55  
  4. Product of values == 3628800 

『注意使用了函數對象的accumulate()的用法。accumulate() 在內部將每個容器中的對象和第三個參數作為multiplies函數對象的參數,multiplies(1,v)計算乘積。VC中的這些模板的源代碼如下:

  1. // TEMPLATE FUNCTION accumulate  
  2. template<class _II, class _Ty> inline 
  3. _Ty accumulate(_II _F, _II _L, _Ty _V)  
  4. {for (; _F != _L; ++_F)  
  5. _V = _V + *_F;  
  6. return (_V); }  
  7. // TEMPLATE FUNCTION accumulate WITH BINOP  
  8. template<class _II, class _Ty, class _Bop> inline 
  9. _Ty accumulate(_II _F, _II _L, _Ty _V, _Bop _B)  
  10. {for (; _F != _L; ++_F)  
  11. _V = _B(_V, *_F);  
  12. return (_V); }  
  13. // TEMPLATE STRUCT binary_function  
  14. template<class _A1, class _A2, class _R>  
  15. struct binary_function {  
  16. typedef _A1 first_argument_type;  
  17. typedef _A2 second_argument_type;  
  18. typedef _R result_type;  
  19. };  
  20. // TEMPLATE STRUCT multiplies  
  21. template<class _Ty>  
  22. struct multiplies : binary_function<_Ty, _Ty, _Ty> {  
  23. _Ty operator()(const _Ty& _X, const _Ty& _Y) const 
  24. {return (_X * _Y); }  
  25. }; 

引言:如果你想深入了解STL到底是怎麼實現的,最好的辦法是寫個簡單的程序,將程序中涉及到的模板源碼給copy下來,稍作整理,就能看懂了。所以沒有必要去買什麼《STL源碼剖析》之類的書籍,那些書可能反而浪費時間。』

1)發生器函數對象

有一類有用的函數對象是“發生器”(generator)。這類函數有自己的內存,也就是說它能夠從先前的調用中記住一個值。例如隨機數發生器函數。

普通的C程序員使用靜態或全局變量 “記憶”上次調用的結果。但這樣做的缺點是該函數無法和它的數據相分離『還有個缺點是要用TLS才能線程安全』。顯然,使用類來封裝一塊:“內存”更安全可靠。先看一下例子:

Listing 9. randfunc.cpp

  1. #include <iostream.h>  
  2. #include <stdlib.h> // Need random(), srandom()  
  3. #include <time.h> // Need time()  
  4. #include <algorithm> // Need random_shuffle()  
  5. #include <vector> // Need vector  
  6. #include <functional> // Need ptr_fun()  
  7. using namespace std;  
  8. // Data to randomize  
  9. int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  
  10. vector<int> v(iarray, iarray + 10);  
  11. // Function prototypes  
  12. void Display(vector<int>& vr, const char *s);  
  13. unsigned int RandInt(const unsigned int n);  
  14. int main()  
  15. {  
  16. srandom( time(NULL) ); // Seed random generator  
  17. Display(v, "Before shuffle:");  
  18. pinter_to_unary_function<unsigned int, unsigned int>  
  19. ptr_RandInt = ptr_fun(RandInt); // Pointer to RandInt()//注意這行  
  20. random_shuffle(v.begin(), v.end(), ptr_RandInt);  
  21. Display(v, "After shuffle:");  
  22. return 0;  
  23. }  
  24. // Display contents of vector vr  
  25. void Display(vector<int>& vr, const char *s)  
  26. {  
  27. cout << endl << s << endl;  
  28. copy(vr.begin(), vr.end(), ostream_iterator<int>(cout, " "));  
  29. cout << endl;  
  30. }  
  31. // Return next random value in sequence modulo n  
  32. unsigned int RandInt(const unsigned int n)  
  33. {  
  34. return random() % n;  

編譯運行結果如下:

  1. $ g++ randfunc.cpp  
  2. $ ./a.out  
  3. Before shuffle:  
  4. 1 2 3 4 5 6 7 8 9 10  
  5. After shuffle:  
  6. 6 7 2 8 3 5 10 1 9 4 

首先用下面的語句申明一個對象:

  1. pointer_to_unary_function<unsigned int, unsigned int>  
  2. ptr_RandInt = ptr_fun(RandInt); 

這兒使用STL的單目函數模板定義了一個變量ptr_RandInt,並將地址初始化到我們的函數RandInt()。單目函數接受一個參數,並返回一個值。現在random_shuffle()可以如下調用:

  1. random_shuffle(v.begin(), v.end(), ptr_RandInt); 

在本例子中,發生器只是簡單的調用rand()函數。

關於常量引用的一點小麻煩不翻譯了,VC下將例子中的const去掉)


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved