STL是C++標(biāo)準(zhǔn)庫的重要組成部分之一,它不僅是一個(gè)可復(fù)用的組件庫,更是一個(gè)包含算法與數(shù)據(jù)結(jié)構(gòu)的軟件框架,同時(shí)也是C++泛型編程的很好例子。STL中運(yùn)用了許多C++的高級技術(shù)。本文介紹函數(shù)對象,其實(shí)是接上一篇的話題,因?yàn)楹瘮?shù)對象本質(zhì)上還是重載操作符。主要參考了《C++ Primer》和《STL源碼剖析》。 可以為類類型的對象重載函數(shù)調(diào)用操作符,定義了調(diào)用操作符的類,其對象稱之為函數(shù)對象(function object),即它們的行為類似函數(shù)對象。這是《C++ Primer》中的定義。下面通過一個(gè)例子來引出函數(shù)對象的使用。在我的解題筆記系列中,有一篇文章 解題筆記(16)——幾個(gè)數(shù)字的問題,其中第四個(gè)問題是調(diào)整數(shù)組順序使奇數(shù)位于偶數(shù)前面(數(shù)組)。輸入一個(gè)整數(shù)數(shù)組,調(diào)整數(shù)組中數(shù)字的順序,使得所有奇數(shù)位于數(shù)組的前半部分。所有偶數(shù)位于數(shù)組的后半部分。要求時(shí)間復(fù)雜度為O(n)。這個(gè)問題不難,其中用到了函數(shù)指針。代碼如下:
- bool IsOdd(int x)
- {
- return (x & 1)? true: false;
- }
-
-
-
- void PartionArray(int *pArray, int nLen, bool (*func)(int))
- {
- int i = -1;
- for(int j = 0; j < nLen; j++)
- {
- if(func(pArray[j]))
- {
- i++;
- int tmp = pArray[j];
- pArray[j] = pArray[i];
- pArray[i] = tmp;
- }
- }
- }
傳遞一個(gè)函數(shù)指針的好處是,可以很方便的修改調(diào)整的條件,這個(gè)問題希望將奇數(shù)放在前面,偶數(shù)放在后面。如果有另外一個(gè)問題,希望將正數(shù)放前面,負(fù)數(shù)放后面,那么只需定義新的調(diào)整函數(shù),類似 IsOdd,然后將它作為參數(shù)傳遞給PartionArray函數(shù)即可。 這里其實(shí)也可以使用函數(shù)對象,代碼如下:
- #include <iostream>
- using namespace std;
-
-
- template<class T>
- struct IsOdd
- {
- bool operator() (T x){
- return (x & 1)?true: false;
- }
- };
-
-
-
- template <class T, class F>
- void PartionArray(T *pArray, int nLen, F func)
- {
- int i = -1;
- for(int j = 0; j < nLen; j++)
- {
- if(func(pArray[j]))
- {
- i++;
- T tmp = pArray[j];
- pArray[j] = pArray[i];
- pArray[i] = tmp;
- }
- }
- }
-
- int main()
- {
- short a[] = {1,2,3,4,5,6};
- long b[] = {1,2,3,4,5,6};
- PartionArray(a, 6, IsOdd<short>());
- PartionArray(b, 6, IsOdd<long>());
- return 0;
- }
相比函數(shù)指針,函數(shù)對象的優(yōu)勢在于能很好的滿足STL的抽象要求,同時(shí)更為靈活。上面這個(gè)例子算是一個(gè)引子,引出STL中函數(shù)對象的運(yùn)用。 STL中的函數(shù)對象并不是孤立的,而是與STL的算法緊密聯(lián)系在一起。舉個(gè)簡單的例子就明白了,比如我們希望利用STL的算法為一個(gè)數(shù)組排序,我們可以這樣做。
- #include <iostream>
- #include <algorithm>
- using namespace std;
- int main()
- {
- int a[] = {1,3,2,4,5,7};
- sort(&a[0], &a[6]);
- for(int i = 0; i < 6; i++)
- cout<<a[i]<<' ';
- cout<<endl;
- return 0;
- }
這個(gè)排序算法默認(rèn)是遞增排序,那么如果希望是遞減排序呢?很方便,在函數(shù)實(shí)參中加入一個(gè)函數(shù)對象即可。記住需包含頭文件 #include <functional>
- #include <iostream>
- #include <functional>
- #include <algorithm>
- using namespace std;
- int main()
- {
- int a[] = {1,3,2,4,5,7};
- sort(&a[0], &a[6], greater<int>());
- for(int i = 0; i < 6; i++)
- cout<<a[i]<<' ';
- cout<<endl;
- return 0;
- }
可以說在STL中,函數(shù)對象扮演著重要的角色,發(fā)揮著巨大的作用。下面列出了幾個(gè)常用的函數(shù)對象,摘自HP的STL源碼,做了部分修改。STL定義了兩個(gè)類,作為一元操作和二元操作的基類,這兩個(gè)基類僅僅是使用了內(nèi)嵌型別技術(shù),為什么要這樣子呢?后面介紹函數(shù)適配器時(shí)有說明,可以看到它的強(qiáng)大之處。
-
- template <class Arg, class Result>
- struct unary_function {
- typedef Arg argument_type;
- typedef Result result_type;
- };
-
- template <class Arg1, class Arg2, class Result>
- struct binary_function {
- typedef Arg1 first_argument_type;
- typedef Arg2 second_argument_type;
- typedef Result result_type;
- };
-
-
- template <class T>
- struct negate : public unary_function<T, T> {
- T operator()(const T& x) const { return -x; }
- };
- template <class T>
- struct logical_not : public unary_function<T,bool> {
- bool operator()(const T& x) const { return !x; }
- };
-
- template <class T>
- struct plus : public binary_function<T, T, T> {
- T operator()(const T& x, const T& y) const { return x + y; }
- };
- template <class T>
- struct minus : public binary_function<T, T, T> {
- T operator()(const T& x, const T& y) const { return x - y; }
- };
- template <class T>
- struct multiplies : public binary_function<T, T , T> {
- T operator()(const T& x, const T& y) const { return x * y; }
- };
- template <class T>
- struct divides : public binary_function<T ,T, T> {
- T operator()(const T& x, const T& y) const { return x / y; }
- };
- template <class T>
- struct modulus : public binary_function<T, T, T> {
- T operator()(const T& x, const T& y) const { return x % y; }
- };
-
- template <class T>
- struct equal_to : public binary_function<T, T, bool> {
- bool operator()(const T& x, const T& y) const { return x == y; }
- };
- template <class T>
- struct not_equal_to : public binary_function<T, T,bool> {
- bool operator()(const T& x, const T& y) const { return x != y; }
- };
- 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 T>
- struct less : public binary_function<T, T, bool> {
- bool operator()(const T& x, const T& y) const { return x < y; }
- };
- template <class T>
- struct greater_equal : public binary_function<T, T, bool> {
- bool operator()(const T& x, const T& y) const { return x >= y; }
- };
- template <class T>
- struct less_equal : public binary_function<T, T, bool> {
- bool operator()(const T& x, const T& y) const { return x <= y; }
- };
-
- template <class T>
- struct logical_and : public binary_function<T, T, bool>{
- bool operator()(const T& x, const T& y) const { return x && y; }
- };
- template <class T>
- struct logical_or : public binary_function<T, T, bool>
- {
- bool operator()(const T& x, const T& y) const { return x || y; }
- };
上面這些函數(shù)對象都比較簡單,接下來介紹幾個(gè)稍微復(fù)雜點(diǎn)的,它們被稱為函數(shù)適配器(function adapter),用于特化和擴(kuò)展一元和二元函數(shù)對象。分為兩類,第一是綁定器(binder),它通過將一個(gè)操作數(shù)綁定到給定值而將二元函數(shù)對象轉(zhuǎn)換為一元函數(shù)。第二是求反器(negator),它將謂詞函數(shù)對象的真值求反。這些定義來自《C++ Primer》一書。書上還給了兩個(gè)例子,這里羅列一下: - #include <iostream>
- #include <functional>
- #include <vector>
- #include <algorithm>
- using namespace std;
- int main()
- {
- vector<int> vec(10, 1);
- int count1 = count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10));
- int count2 = count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10)));
- cout<<count1<<' '<<count2<<endl;
- return 0;
- }
下面分析一下STL是如何實(shí)現(xiàn)函數(shù)適配器的,先給出STL的源碼。已經(jīng)整理過了。
-
- template <class Operation>
- class binder1st: public unary_function<typename Operation::second_argument_type, typename Operation::result_type> {
- public:
- binder1st(const Operation& x, const typename Operation::first_argument_type& y) : op(x), value(y) {}
- typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const {
- return op(value, x);
- }
- protected:
- Operation op;
- typename Operation::first_argument_type value;
- };
-
- template <class Operation>
- class binder2nd: public unary_function<typename Operation::first_argument_type,typename Operation::result_type> {
- public:
- binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {}
- typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const {
- return op(x, value);
- }
- protected:
- Operation op;
- typename Operation::second_argument_type value;
- };
-
- template <class Operation, class T>
- inline binder1st<Operation> bind1st(const Operation& fn, const T& x) {
- typedef typename Operation::first_argument_type Arg1_type;
- return binder1st<Operation>(fn,Arg1_type(x));
- }
-
- template <class Operation, class T>
- inline binder2nd<Operation> bind2nd(const Operation& fn, const T& x) {
- typedef typename Operation::second_argument_type Arg2_type;
- return binder2nd<Operation>(fn, Arg2_type(x));
- }
-
- template <class Predicate>
- class unary_negate: public unary_function<typename Predicate::argument_type, bool> {
- protected:
- Predicate pred;
- public:
- explicit unary_negate(const Predicate& x) : pred(x) {}
- bool operator()(const typename Predicate::argument_type& x) const {
- return !pred(x);
- }
- };
-
- template <class Predicate>
- class binary_negate : public binary_function<typename Predicate::first_argument_type, typename Predicate::second_argument_type,bool> {
- protected:
- Predicate pred;
- public:
- explicit binary_negate(const Predicate& x) : pred(x) {}
- bool operator()(const typename Predicate::first_argument_type& x, const typename Predicate::second_argument_type& y) const {
- return !pred(x, y);
- }
- };
-
- template <class Predicate>
- inline unary_negate<Predicate> not1(const Predicate& pred)
- {
- return unary_negate<Predicate>(pred);
- }
-
- template <class Predicate>
- inline binary_negate<Predicate> not2(const Predicate& pred)
- {
- return binary_negate<Predicate>(pred);
- }
到這里,我們可以回答之前的一個(gè)問題,就是STL為什么要定義兩個(gè)基類,里面僅僅是內(nèi)嵌型別。通過上面代碼,不難發(fā)現(xiàn),原來是用來萃取函數(shù)對象所操作的數(shù)據(jù)類型。比如 binder1st 這個(gè)類,它的模板中只有一個(gè)形參,它繼承自unary_function,而unary_function的模板有兩個(gè)形參。如何實(shí)現(xiàn)這種繼承關(guān)系呢?內(nèi)嵌型別技術(shù),因?yàn)閎inder1st 的模板實(shí)參是個(gè)函數(shù)對象,繼承自unary_function或binary_function,通過內(nèi)嵌型別技術(shù)很容易萃取出數(shù)據(jù)類型。 再進(jìn)一步,函數(shù)適配器是如何工作的呢? - include <iostream>
- #include <functional>
- #include <vector>
- #include <algorithm>
- using namespace std;
- int main()
- {
- vector<int> vec(10, 1);
- int count1 = count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10));
- int count2 = count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10)));
- cout<<count1<<' '<<count2<<endl;
- return 0;
- }
還是以這個(gè)程序?yàn)槔榻B一下count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10))具體執(zhí)行。首先執(zhí)行bind2nd(less_equal<int>(), 10)這個(gè)函數(shù),這個(gè)函數(shù)會(huì)返回一個(gè)binder2nd的函數(shù)對象,注意構(gòu)造這個(gè)函數(shù)對象的時(shí)候,binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} 。第二個(gè)參數(shù)value的值設(shè)置為10,而第一個(gè)參數(shù)op設(shè)置成less_equal<int>()這個(gè)函數(shù)對象。 接著count_if 執(zhí)行時(shí),下面是它的源碼。執(zhí)行時(shí),pred(*first)其實(shí)就是binder2nd中的運(yùn)算,而這個(gè)運(yùn)算的第二個(gè)參數(shù)是固定的,也就是10,所以pred只傳遞了一個(gè)實(shí)參進(jìn)去就可以了。 - template <class InputIter, class Predicate, class Size>
- void count_if(InputIter first, InputIter last, Predicate pred, Size& n) {
- for ( ; first != last; ++first)
- if (pred(*first))
- ++n;
- }
- template <class InputIter, class Predicate>
- typename iterator_traits<InputIter>::difference_type count_if(InputIter first, InputIter last, Predicate pred) {
- typename iterator_traits<InputIter>::difference_type n = 0;
- for ( ; first != last; ++first)
- if (pred(*first))
- ++n;
- return n;
- }
下面把上述綜合起來,通過修改STL,寫個(gè)完整的測試程序。如下所示,注意這里用的都是自定義的代碼,沒有包含STL的函數(shù)對象和函數(shù)。
- #include <iostream>
- #include <vector>
- using namespace std;
-
-
- template <class InputIter, class Predicate, class Size>
- void count_if(InputIter first, InputIter last, Predicate pred, Size& n) {
- for ( ; first != last; ++first)
- if (pred(*first))
- ++n;
- }
-
- template <class Arg, class Result>
- struct unary_function {
- typedef Arg argument_type;
- typedef Result result_type;
- };
-
- template <class Arg1, class Arg2, class Result>
- struct binary_function {
- typedef Arg1 first_argument_type;
- typedef Arg2 second_argument_type;
- typedef Result result_type;
- };
-
- template <class T>
- struct less_equal : public binary_function<T, T, bool> {
- bool operator()(const T& x, const T& y) const { return x <= y; }
- };
-
- template <class Operation>
- class binder2nd: public unary_function<typename Operation::first_argument_type,typename Operation::result_type> {
- public:
- binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) { cout<<"binder2nd Constructor"<<endl; }
- typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const {
- cout<<"binder2nd's operator()"<<endl;
- return op(x, value);
- }
- protected:
- Operation op;
- typename Operation::second_argument_type value;
- };
-
- template <class Operation, class T>
- inline binder2nd<Operation> bind2nd(const Operation& fn, const T& x) {
- typedef typename Operation::second_argument_type Arg2_type;
- return binder2nd<Operation>(fn, Arg2_type(x));
- }
-
- template <class Predicate>
- class unary_negate: public unary_function<typename Predicate::argument_type, bool> {
- protected:
- Predicate pred;
- public:
- explicit unary_negate(const Predicate& x) : pred(x) { cout<<"unary_negate Constructor"<<endl; }
- bool operator()(const typename Predicate::argument_type& x) const {
- cout<<"unary_negate's operator()"<<endl;
- return !pred(x);
- }
- };
-
- template <class Predicate>
- inline unary_negate<Predicate> not1(const Predicate& pred)
- {
- return unary_negate<Predicate>(pred);
- }
-
- int main()
- {
- vector<int> vec(10, 1);
- int count1 = 0, count2 = 0;
- count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10),count1);
- count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10)),count2);
- cout<<count1<<' '<<count2<<endl;
- return 0;
- }
出處 http://blog.csdn.net/wuzhekai1985
原文: http://blog.csdn.net/wuzhekai1985/article/details/6658940
|