C++ STL 學習 :for_each與仿函數(functor)


  簡單來將,仿函數(functor)就是一個重載了"()"運算符的struct或class,利用對象支持operator()的特性,來達到模擬函數調用效果的技術。
  我們平時對一個集合類遍歷的時候,例如vector,是這樣做的:
for(vector<int>::const_iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
{
    //do your whatever you want here
}
例如下面的代碼:
#include <vector>
#include <iostream>

struct State
{
    State( int state ) : m_state( state ){}
    ~State() { std::cout << "~State(), m_state=" << m_state << std::endl; }

    void setState( int state ){ m_state = state; }
    int getState() const{ return m_state; }

    void print() const { std::cout << "State::print: " << m_state << std::endl; }

private:
    int m_state;
};

int main()
{
    std::vector<State*> vect;

    vect.push_back( new State(0) );
    vect.push_back( new State(1) );
    vect.push_back( new State(2) );
    vect.push_back( new State(3) );

       std::vector<State*>::iterator it( vect.begin() );
    std::vector<State*>::iterator ite( vect.end() );
    for ( ; it != ite; ++it )
    {
        (*it)->print();
    }
    
    
    system( "pause" );
    return 0;
}

這里的for循環語句有點冗余,想到了std::for_each ,為了使用for_each,我們需要定義一個函數,如下:

void print( State* pstate )
{
    pstate->print();
}

於是就可以簡化為下面代碼:
std::for_each( vect.begin(), vect.end(), &print );

上面這段代碼有點丑陋,看起來不太爽,主要是函數指針的原因。
在這種應用環境下,C++有仿函數來替代,我們定義一個仿函數,如下:

struct Printer
{
    template<typename T> void operator()( T* t ) { t->print(); }
};

於是就可以簡化為下面代碼:
std::for_each( vect.begin(), vect.end(), Printer() );

下面,我們初步看下 for_each 的STL源碼實現: 

 // TEMPLATE FUNCTION for_each

template<class _InIt,
    class _Fn1> inline
    _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {    // perform function for each element

    _DEBUG_RANGE(_First, _Last);
    _DEBUG_POINTER(_Func);
    _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
    _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
    for (; _ChkFirst != _ChkLast; ++_ChkFirst)
        _Func(*_ChkFirst);
    return (_Func);
    }
    
上面的代碼看起來挺暈菜的,這里給出 effective STL 里面的一個實現,簡單明了:

template< typename InputIterator, typename Function >
Function for_each( InputIterator beg, InputIterator end, Function f ) {
    while ( beg != end )
        f( *beg++ );
} // TEMPLATE FUNCTION for_each

template<class _InIt,
    class _Fn1> inline
    _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {    // perform function for each element

    _DEBUG_RANGE(_First, _Last);
    _DEBUG_POINTER(_Func);
    _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
    _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
    for (; _ChkFirst != _ChkLast; ++_ChkFirst)
        _Func(*_ChkFirst);
    return (_Func);
    }
    
上面的代碼看起來挺暈菜的,這里給出 effective STL 里面的一個實現,簡單明了:

template< typename InputIterator, typename Function >
Function for_each( InputIterator beg, InputIterator end, Function f ) {
    while ( beg != end )
        f( *beg++ );
}

  其實for_each就是一個模板函數,將for循環語句封裝起來,前面兩個參數都是迭代器,第三個參數是使用一個函數指針(或仿函數),其功能是對每一個迭代器所指向的值調用仿函數。

上面代碼還是有點冗余,因為為了使用for_each還要單獨定義一個函數(或仿函數),不太清爽,
呵呵,stl早為我們准備好了 mem_fun 模板函數來解決這個一個問題,於是代碼再次簡化為:

std::for_each( vect.begin(), vect.end(), std::mem_fun( &State::print ) );

我們一起看看 mem_fun 的STL源碼實現: 

       // TEMPLATE FUNCTION mem_fun
template<class _Result,
    class _Ty> inline
    mem_fun_t<_Result, _Ty> mem_fun(_Result (_Ty::*_Pm)())
    {    // return a mem_fun_t functor adapter
    return (std::mem_fun_t<_Result, _Ty>(_Pm));
    }

mem_fun 函數實際上是調用 mem_fun_t 函數,我們接着深入看看 mem_fun_t,


        // TEMPLATE CLASS mem_fun_t
template<class _Result,
    class _Ty>
    class mem_fun_t
        : public unary_function<_Ty *, _Result>
    {    // functor adapter (*p->*pfunc)(), non-const *pfunc
public:
    explicit mem_fun_t(_Result (_Ty::*_Pm)())
        : _Pmemfun(_Pm)
        {    // construct from pointer
        }

    _Result operator()(_Ty *_Pleft) const
        {    // call function
        return ((_Pleft->*_Pmemfun)());
        }
private:
    _Result (_Ty::*_Pmemfun)();    // the member function pointer
    };

將上面這段代碼定義的寫的我們好看懂一點,如下:

        // TEMPLATE CLASS mem_fun_t
template< typename _Result, typename _Ty >
class mem_fun_t : public unary_function<_Ty *, _Result>
{    
    typedef _Result (_Ty::*_Pmemfun)();
public:
    explicit mem_fun_t( _Pmemfun& pfunc )
        : m_pfun( pfunc )
    {    // construct from pointer
    }

    _Result operator()(_Ty *_Pleft) const
    {    // call function
        return ( (_Pleft->*m_pfun)() );
    }

private:
    _Pmemfun m_pfun; // the member function pointer

};

  這樣就比較清晰了,定義了仿函數mem_fun_t內部定義了一個類成員函數指針,仿函數構造的時候將函數指針保存起來,當仿函數operator()被調用的時候,就通過與一個類的實例關聯起來從而實現了類成員函數的調用。

  其調用流程是這樣的,for_each把vector中的元素傳送給mem_fun,mem_fun自己產生一個仿函數mem_fun_t,然后仿函數調用其重載的()。

  上述源碼還有最后一個沒有說明,就是unary_function,直接上源碼:

 // TEMPLATE STRUCT unary_function
template<class _Arg,
    class _Result>
    struct unary_function
    {    // base class for unary functions
    typedef _Arg argument_type;
    typedef _Result result_type;
    };

就一個模板結構體。沒有數據成員,非常簡單。
最后,定義一個刪除指針的仿函數:
struct DeletePointer
{
    template<typename T> void operator()( T* ptr ) const { delete ptr; }
};
然后調用,就一個逐一刪除vector里面的所有元素了。
std::for_each( vect.begin(), vect.end(), DeletePointer() );


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM