仿函數,又叫做函數對象,是一個重載了"()"運算符的struct,是STL(標准模板庫)六大組件(容器、配置器、迭代器、算法、配接器、仿函數)之一;仿函數雖然小,但卻極大的拓展了算法的功能,幾乎所有的算法都有仿函數版本。例如,查找算法find_if就是對find算法的擴展,標准的查找是兩個元素向等就找到了,但是什麼是相等在不同情況下卻需要不同的定義,如地址相等,地址和郵編都相等,雖然這些相等的定義在變,但算法本身卻不需要改變,這都多虧了仿函數。
看個簡單的例子:
struct D {
D(int i=0){num=i;}
int num;
};
struct print_D{
void operator()(const D* d)const{
cout<<"I am D. my num="<<d->num<<endl;
}
};
int main()
{
vector<D*> V;
V.push_back(new D(1));
V.push_back(new D(2));
V.push_back(new D);
V.push_back(new D(3));
for_each(V.begin(), V.end(), print_D());
}
編譯輸出:
I am D. my num=1
I am D. my num=2
I am D. my num=0
I am D. my num=3
如果使用mem_fun,會方便很多:
struct D {
D(int i=0){num=i;}
void print() { cout << "I'm a D. my num=" << num<< endl; }
int num;
};
int main()
{
vector<D*> V;
V.push_back(new D(1));
V.push_back(new D(2));
V.push_back(new D);
V.push_back(new D(3));
for_each(V.begin(), V.end(), mem_fun(&D::print));
}
mem_fun對於一些多態的虛函數也十分有用
struct B {
virtual void print() = 0;
};
struct D1 : public B {
void print() { cout << "I'm a D1" << endl; }
};
struct D2 : public B {
void print() { cout << "I'm a D2" << endl; }
};
int main()
{
vector<B*> V;
V.push_back(new D1);
V.push_back(new D2);
V.push_back(new D2);
V.push_back(new D1);
for_each(V.begin(), V.end(), mem_fun(&B::print));
}
仿函數之所以叫做函數對象,是因為仿函數都是定義了()函數運算操作符的類。例如,STL自帶的仿函數equal_to<class Tp>定義為:
template <class _Tp>
struct equal_to : public binary_function<_Tp,_Tp,bool>
{
bool operator()(const_Tp&__x,const_Tp&__y) const { return __x==__y; }
};
在算法內部調用此操作符,如find_if:
template <class_RandomAccessIter,class_Predicate>
_STLP_INLINE_LOOP _RandomAccessIter __find_if(_RandomAccessIter __first, _RandomAccessIter __last,_Predicate __pred,const random_access_iterator_tag &)
{
_STLP_DIFFERENCE_TYPE(_RandomAccessIter) __trip_count = (__last - __first) >> 2;
for ( ; __trip_count > 0 ; --__trip_count) {
if (__pred(*__first)) return __first;
++__first;
…
//以下略
}
仿函數的可配接性
仿函數的可配接性是指仿函數能夠與其它仿函數配接在一起實現新的功能,如不小於60,可以利用STL自帶的not1<int>和less<int>配接而成:not1(bind2nd(less<int>(), 12))。
一般而言,通用函數也可以作為仿函數參數傳遞給算法,但其區別在於“通用函數不具有可配接性”。是否定義成仿函數都具有配接性了呢?也不盡然!只有從unary_function或者binary_funcion繼承的仿函數才有配接性。這是為什麼呢?
其奧妙在於模板類常見的類型定義,可配接性的關鍵就在於這些類型定義;如binary_function:
template <class _Arg1, class _Arg2, class _Result>
struct binary_function {
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
在STL的適配器中會自動使用到這些類型定義,所以必須聲明這些類型。
把通用函數轉換為仿函數
STL的實現也考慮到會將通用函數作為仿函數來使用,為了保證這些函數的可配接性,即把這些函數轉換為仿函數使用,STL也提供了相應的適配器ptr_fun1_base,ptr_fun2_base,其原理也是重載函數調用操作符,在仿函數對象構造時把通用函數作為參數傳入,如:
template <class _Arg, class _Result>
class pointer_to_unary_function : public unary_function<_Arg, _Result>
{
protected:
//函數原型
_Result (*_M_ptr)(_Arg);
public:
pointer_to_unary_function() {}
//構造時把函數指針傳入
explicit pointer_to_unary_function(_Result (*__x)(_Arg)) : _M_ptr(__x) {}
//()函數運算操作符重載,執行函數功能www.2cto.com
_Result operator()(_Arg __x) const { return _M_ptr(__x); }
};
作者:zhongguoren666