自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

STL組件之算法

開(kāi)發(fā) 后端 算法
我們都知道STL的三大組件是迭代器,算法和容器。本文介紹STL組件中的一個(gè)算法,希望對(duì)你有幫助,一起來(lái)看。

STL提供了大量的模板類(lèi)和函數(shù),可以在OOP和常規(guī)編程中使用。所有的STL的大約50個(gè)算法都是完全通用的,而且不依賴(lài)于任何特定的數(shù)據(jù)類(lèi)型。下面的小節(jié)說(shuō)明了三個(gè)基本的STL組件:

1)迭代器提供了訪問(wèn)容器中對(duì)象的方法。例如,可以使用一對(duì)迭代器指定list或vector中的一定范圍的對(duì)象。迭代器就如同一個(gè)指針。事實(shí)上,C++的指針也是一種迭代器。但是,迭代器也可以是那些定義了operator*()以及其他類(lèi)似于指針的操作符地方法的類(lèi)對(duì)象。

2)容器是一種數(shù)據(jù)結(jié)構(gòu),如list,vector,和deques ,以模板類(lèi)的方法提供。為了訪問(wèn)容器中的數(shù)據(jù),可以使用由容器類(lèi)輸出的迭代器。

3)算法是用來(lái)操作容器中的數(shù)據(jù)的模板函數(shù)。例如,STL用sort()來(lái)對(duì)一個(gè)vector中的數(shù)據(jù)進(jìn)行排序,用find()來(lái)搜索一個(gè)list中的對(duì)象。函數(shù)本身與他們操作的數(shù)據(jù)的結(jié)構(gòu)和類(lèi)型無(wú)關(guān),因此他們可以在從簡(jiǎn)單數(shù)組到高度復(fù)雜容器的任何數(shù)據(jù)結(jié)構(gòu)上使用。

函數(shù)和函數(shù)對(duì)象

STL中,函數(shù)被稱(chēng)為算法,也就是說(shuō)它們和標(biāo)準(zhǔn)C庫(kù)函數(shù)相比,它們更為通用。STL算法通過(guò)重載operator()函數(shù)實(shí)現(xiàn)為模板類(lèi)或模板函數(shù)。這些類(lèi)用于創(chuàng)建函數(shù)對(duì)象,對(duì)容器中的數(shù)據(jù)進(jìn)行各種各樣的操作。下面的幾節(jié)解釋如何使用函數(shù)和函數(shù)對(duì)象。

函數(shù)和斷言

經(jīng)常需要對(duì)容器中的數(shù)據(jù)進(jìn)行用戶(hù)自定義的操作。例如,你可能希望遍歷一個(gè)容器中所有對(duì)象的STL算法能夠回調(diào)自己的函數(shù)。例如

 

  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);//調(diào)用普通函數(shù)  
  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);//調(diào)用斷言函數(shù)  
  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);  

 

所謂斷言函數(shù),就是返回bool值的函數(shù)。

函數(shù)對(duì)象

除了給STL算法傳遞一個(gè)回調(diào)函數(shù),你還可能需要傳遞一個(gè)類(lèi)對(duì)象以便執(zhí)行更復(fù)雜的操作。這樣的一個(gè)對(duì)象就叫做函數(shù)對(duì)象。實(shí)際上函數(shù)對(duì)象就是一個(gè)類(lèi),但它和回調(diào)函數(shù)一樣可以被回調(diào)。例如,在函數(shù)對(duì)象每次被for_each()或find_if()函數(shù)調(diào)用時(shí)可以保留統(tǒng)計(jì)信息。函數(shù)對(duì)象是通過(guò)重載 operator()()實(shí)現(xiàn)的。如果TanyClass定義了opeator()(),那么就可以這么使用:

 

  1. TAnyClass object; // Construct object  
  2. object(); // Calls TAnyClass::operator()() function  
  3. for_each(v.begin(), v.end(), object); 

STL定義了幾個(gè)函數(shù)對(duì)象。由于它們是模板,所以能夠用于任何類(lèi)型,包括C/C++固有的數(shù)據(jù)類(lèi)型,如long。有些函數(shù)對(duì)象從名字中就可以看出它的用途,如plus()和multiplies()。類(lèi)似的greater()和less-equal()用于比較兩個(gè)值。

注意

有些版本的ANSI C++定義了times()函數(shù)對(duì)象,而GNU C++把它命名為multiplies()。使用時(shí)必須包含頭文件<functional>。

一個(gè)有用的函數(shù)對(duì)象的應(yīng)用是accumulate() 算法。該函數(shù)計(jì)算容器中所有值的總和。記住這樣的值不一定是簡(jiǎn)單的類(lèi)型,通過(guò)重載operator+(),也可以是類(lèi)對(duì)象。

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 

『注意使用了函數(shù)對(duì)象的accumulate()的用法。accumulate() 在內(nèi)部將每個(gè)容器中的對(duì)象和第三個(gè)參數(shù)作為multiplies函數(shù)對(duì)象的參數(shù),multiplies(1,v)計(jì)算乘積。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到底是怎么實(shí)現(xiàn)的,***的辦法是寫(xiě)個(gè)簡(jiǎn)單的程序,將程序中涉及到的模板源碼給copy下來(lái),稍作整理,就能看懂了。所以沒(méi)有必要去買(mǎi)什么《STL源碼剖析》之類(lèi)的書(shū)籍,那些書(shū)可能反而浪費(fèi)時(shí)間。』

(1)發(fā)生器函數(shù)對(duì)象

有一類(lèi)有用的函數(shù)對(duì)象是“發(fā)生器”(generator)。這類(lèi)函數(shù)有自己的內(nèi)存,也就是說(shuō)它能夠從先前的調(diào)用中記住一個(gè)值。例如隨機(jī)數(shù)發(fā)生器函數(shù)。

普通的C程序員使用靜態(tài)或全局變量 “記憶”上次調(diào)用的結(jié)果。但這樣做的缺點(diǎn)是該函數(shù)無(wú)法和它的數(shù)據(jù)相分離『還有個(gè)缺點(diǎn)是要用TLS才能線程安全』。顯然,使用類(lèi)來(lái)封裝一塊:“內(nèi)存”更安全可靠。先看一下例子:

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;  

編譯運(yùn)行結(jié)果如下:

 

  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 

首先用下面的語(yǔ)句申明一個(gè)對(duì)象:

 

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

這兒使用STL的單目函數(shù)模板定義了一個(gè)變量ptr_RandInt,并將地址初始化到我們的函數(shù)RandInt()。單目函數(shù)接受一個(gè)參數(shù),并返回一個(gè)值?,F(xiàn)在random_shuffle()可以如下調(diào)用:

 

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

在本例子中,發(fā)生器只是簡(jiǎn)單的調(diào)用rand()函數(shù)。

關(guān)于常量引用的一點(diǎn)小麻煩(不翻譯了,VC下將例子中的const去掉)

#p#

(2)發(fā)生器函數(shù)類(lèi)對(duì)象

下面的例子說(shuō)明發(fā)生器函數(shù)類(lèi)對(duì)象的使用。

Listing 10. fiborand.cpp

 

  1. #include <iostream.h>  
  2. #include <algorithm> // Need random_shuffle()  
  3. #include <vector> // Need vector  
  4. #include <functional> // Need unary_function  
  5. using namespace std;  
  6. // Data to randomize  
  7. int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  
  8. vector<int> v(iarray, iarray + 10);  
  9. // Function prototype  
  10. void Display(vector<int>& vr, const char *s);  
  11. // The FiboRand template function-object class  
  12. template <class Arg>  
  13. class FiboRand : public unary_function<Arg, Arg> {  
  14. int i, j;  
  15. Arg sequence[18];  
  16. public:  
  17. FiboRand();  
  18. Arg operator()(const Arg& arg);  
  19. };  
  20. void main()  
  21. {  
  22. FiboRand<int> fibogen; // Construct generator object  
  23. cout << "Fibonacci random number generator" << endl;  
  24. cout << "using random_shuffle and a function object" << endl;  
  25. Display(v, "Before shuffle:");  
  26. random_shuffle(v.begin(), v.end(), fibogen);  
  27. Display(v, "After shuffle:");  
  28. }  
  29. // Display contents of vector vr  
  30. void Display(vector<int>& vr, const char *s)  
  31. {  
  32. cout << endl << s << endl;  
  33. copy(vr.begin(), vr.end(),  
  34. ostream_iterator<int>(cout, " "));  
  35. cout << endl;  
  36. }  
  37. // FiboRand class constructor  
  38. template<class Arg>  
  39. FiboRand<Arg>::FiboRand()  
  40. {  
  41. sequence[17] = 1;  
  42. sequence[16] = 2;  
  43. for (int n = 15; n > 0; n—)  
  44. sequence[n] = sequence[n + 1] + sequence[n + 2];  
  45. i = 17;  
  46. j = 5;  
  47. }  
  48. // FiboRand class function operator  
  49. template<class Arg>  
  50. Arg FiboRand<Arg>::operator()(const Arg& arg)  
  51. {  
  52. Arg k = sequence[i] + sequence[j];  
  53. sequence[i] = k;  
  54. i--;  
  55. j--;  
  56. if (i == 0) i = 17;  
  57. if (j == 0) j = 17;  
  58. return k % arg;  

編譯運(yùn)行輸出如下:

 

  1. $ g++ fiborand.cpp  
  2. $ ./a.out  
  3. Fibonacci random number generator  
  4. using random_shuffle and a function object  
  5. Before shuffle:  
  6. 1 2 3 4 5 6 7 8 9 10  
  7. After shuffle:  
  8. 6 8 5 4 3 7 10 1 9 

該程序用完全不通的方法使用使用rand_shuffle。Fibonacci 發(fā)生器封裝在一個(gè)類(lèi)中,該類(lèi)能從先前的“使用”中記憶運(yùn)行結(jié)果。在本例中,類(lèi)FiboRand 維護(hù)了一個(gè)數(shù)組和兩個(gè)索引變量I和j。

FiboRand類(lèi)繼承自u(píng)nary_function() 模板:

 

  1. template <class Arg>  
  2. class FiboRand : public unary_function<Arg, Arg> {... 

Arg是用戶(hù)自定義數(shù)據(jù)類(lèi)型。該類(lèi)還定以了兩個(gè)成員函數(shù),一個(gè)是構(gòu)造函數(shù),另一個(gè)是operator()()函數(shù),該操作符允許random_shuffle()算法象一個(gè)函數(shù)一樣“調(diào)用”一個(gè)FiboRand對(duì)象。

#p#

(3)綁定器函數(shù)對(duì)象

一個(gè)綁定器使用另一個(gè)函數(shù)對(duì)象f()和參數(shù)值V創(chuàng)建一個(gè)函數(shù)對(duì)象。被綁定函數(shù)對(duì)象必須為雙目函數(shù),也就是說(shuō)有兩個(gè)參數(shù),A和B。STL 中的幫定器有:

  • bind1st() 創(chuàng)建一個(gè)函數(shù)對(duì)象,該函數(shù)對(duì)象將值V作為***個(gè)參數(shù)A。
  • bind2nd()創(chuàng)建一個(gè)函數(shù)對(duì)象,該函數(shù)對(duì)象將值V作為第二個(gè)參數(shù)B。

舉例如下:

Listing 11. binder.cpp

 

  1. #include <iostream.h>  
  2. #include <algorithm>  
  3. #include <functional>  
  4. #include <list>  
  5. using namespace std;  
  6. // Data  
  7. int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  
  8. list<int> aList(iarray, iarray + 10);  
  9. int main()  
  10. {  
  11. int k = 0;  
  12. count_if(aList.begin(), aList.end(),  
  13. bind1st(greater<int>(), 8), k);  
  14. cout << "Number elements < 8 == " << k << endl;  
  15. return 0;  

Algorithm count_if()計(jì)算滿(mǎn)足特定條件的元素的數(shù)目。 這是通過(guò)將一個(gè)函數(shù)對(duì)象和一個(gè)參數(shù)捆綁到為一個(gè)對(duì)象,并將該對(duì)象作為算法的第三個(gè)參數(shù)實(shí)現(xiàn)的。 注意這個(gè)表達(dá)式:

 

  1. bind1st(greater<int>(), 8) 

該表達(dá)式將greater<int>()和一個(gè)參數(shù)值8捆綁為一個(gè)函數(shù)對(duì)象。由于使用了bind1st(),所以該函數(shù)相當(dāng)于計(jì)算下述表達(dá)式:

8 > q

表達(dá)式中的q是容器中的對(duì)象。因此,完整的表達(dá)式

 

  1. count_if(aList.begin(), aList.end(),  
  2. bind1st(greater<int>(), 8), k); 

計(jì)算所有小于或等于8的對(duì)象的數(shù)目。

(4)否定函數(shù)對(duì)象

所謂否定(negator)函數(shù)對(duì)象,就是它從另一個(gè)函數(shù)對(duì)象創(chuàng)建而來(lái),如果原先的函數(shù)返回真,則否定函數(shù)對(duì)象返回假。有兩個(gè)否定函數(shù)對(duì)象:not1()和 not2()。not1()接受單目函數(shù)對(duì)象,not2()接受雙目函數(shù)對(duì)象。否定函數(shù)對(duì)象通常和幫定器一起使用。例如,上節(jié)中用bind1nd來(lái)搜索 q<=8的值:

 

  1. count_if(aList.begin(), aList.end(),  
  2. bind1st(greater<int>(), 8), k); 

如果要搜索q>8的對(duì)象,則用bind2st。而現(xiàn)在可以這樣寫(xiě):

 

  1. start = find_if(aList.begin(), aList.end(),  
  2. not1(bind1nd(greater<int>(), 6))); 

你必須使用not1,因?yàn)閎ind1nd返回單目函數(shù)。

總結(jié):使用標(biāo)準(zhǔn)模板庫(kù) (STL)

盡管很多程序員仍然在使用標(biāo)準(zhǔn)C函數(shù),但是這就好像騎著毛驢尋找Mercedes一樣。你當(dāng)然最終也會(huì)到達(dá)目標(biāo),但是你浪費(fèi)了很多時(shí)間。

盡管有時(shí)候使用標(biāo)準(zhǔn)C函數(shù)確實(shí)方便(如使用sprintf()進(jìn)行格式化輸出)。但是C函數(shù)不使用異常機(jī)制來(lái)報(bào)告錯(cuò)誤,也不適合處理新的數(shù)據(jù)類(lèi)型。而且標(biāo)準(zhǔn)C函數(shù)經(jīng)常使用內(nèi)存分配技術(shù),沒(méi)有經(jīng)驗(yàn)的程序員很容易寫(xiě)出bug來(lái)。.

C++標(biāo)準(zhǔn)庫(kù)則提供了更為安全,更為靈活的數(shù)據(jù)集處理方式。STL最初由HP實(shí)驗(yàn)室的Alexander Stepanov和Meng Lee開(kāi)發(fā)。最近,C++標(biāo)準(zhǔn)委員會(huì)采納了STL,盡管在不同的實(shí)現(xiàn)之間仍有細(xì)節(jié)差別。

STL的最主要的兩個(gè)特點(diǎn):數(shù)據(jù)結(jié)構(gòu)和算法的分離,非面向?qū)ο蟊举|(zhì)。訪問(wèn)對(duì)象是通過(guò)象指針一樣的迭代器實(shí)現(xiàn)的;容器是象鏈表,矢量之類(lèi)的數(shù)據(jù)結(jié)構(gòu),并按模板方式提供;算法是函數(shù)模板,用于操作容器中的數(shù)據(jù)。由于STL以模板為基礎(chǔ),所以能用于任何數(shù)據(jù)類(lèi)型和結(jié)構(gòu)。

【編輯推薦】

  1. 檢測(cè)C++中的內(nèi)存泄漏
  2. c/c++基礎(chǔ) 預(yù)處理指令總結(jié)
  3. 詳細(xì)介紹c++中的類(lèi)對(duì)象內(nèi)存模型
  4. C++基礎(chǔ) 詳細(xì)介紹const的用法
  5. C++初學(xué)者 const使用詳解

 

責(zé)任編輯:于鐵 來(lái)源: 互聯(lián)網(wǎng)
相關(guān)推薦

2011-07-13 13:56:06

STL迭代器

2024-03-04 00:15:00

C++STL算法

2011-07-13 15:07:48

STLC++

2011-07-13 14:49:31

STLC++

2023-12-10 22:00:47

STLC++編程

2011-01-18 16:32:02

Ubuntu

2021-07-09 09:12:40

STL排序算法

2011-07-13 14:58:53

STL容器

2021-11-01 10:21:36

鴻蒙HarmonyOS應(yīng)用

2021-11-05 22:47:44

冒泡排序選擇插入

2021-03-23 13:55:35

大數(shù)據(jù)算法

2021-10-14 15:14:36

鴻蒙HarmonyOS應(yīng)用

2011-07-11 15:26:49

性能優(yōu)化算法

2011-05-07 16:07:32

復(fù)合機(jī)

2020-11-04 10:20:56

嵌入式算法CRC

2022-03-10 08:59:59

傅里葉變換算法系統(tǒng)

2015-04-22 10:57:22

androidSwipeRefres

2021-09-05 07:35:58

lifecycleAndroid組件原理

2021-02-22 21:49:33

Vue動(dòng)態(tài)組件

2022-08-03 09:58:03

跨端框架實(shí)踐
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)