C++ STL 学习 :for_each与仿函数(functor)(一)

来源:互联网 发布:游族网络vip殿堂 编辑:程序博客网 时间:2024/05/23 19:17
C++ STL 学习 :for_each与仿函数(functor)(一)

By zieckey( All right reserved!)

先看wikipedia定义:
A function object, also called a functor, functional, or functionoid,[1]  is a computer programming construct allowing an object to be invoked or called like it was an ordinary function, usually with the same syntax.
简单来将,仿函数(functor)就是一个重载了"()"运算符的struct或class,利用对象支持operator()的特性,来达到模拟函数调用效果的技术。
我们平时对一个集合类遍历的时候,例如vector,是这样做的:
for(vector::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 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++ );
}


其实for_each就是一个模板函数,将for循环语句封装起来,前面两个参数都是迭代器,第三个参数是使用一个函数指针(或仿函数),
其功能是对每一个迭代器所指向的值调用仿函数。之前觉得for_each挺神秘的,其实看看源码也挺简单的。呵呵。


上面代码还是有点冗余,因为为了使用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 void operator()( T* ptr ) const { delete ptr; }
};
然后调用,就一个逐一删除vector里面的所有元素了。
std::for_each( vect.begin(), vect.end(), DeletePointer() );

源码:


 

#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>

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;
};

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

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

struct DeletePointer
{
    template<typename T> void operator()( T* ptr ) const { delete ptr; }
};

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();
    }

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

    system( "pause" );
    return 0;
}


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 四年级数学差生怎么办 给学生讲错题了怎么办 高中作弊被通报怎么办 没收到电子发票怎么办 皇冠草叶子发黄怎么办 脚痒怎么办小窍门 孩子天天玩手机怎么办 征信大数据花了怎么办 衣服容易有水印怎么办? ps楷体有锯齿怎么办 报表打印需要jre怎么办 考试忘记写名字怎么办 ems明信片没收到怎么办 580escl解锁失败怎么办 留学回国人员证明怎么办 员工不签劳动合同怎么办 在日本怎么办韩国签证 在新疆怎么办英国签证 安徽省会考没过怎么办 初中要会考了怎么办 担保人卡被冻结怎么办 188a转永居失败怎么办 刷机忘了id密码怎么办 在澳大利亚怎么办白卡 外国的工作签证怎么办 北京去澳门怎么办签注 专科挂科毕业证怎么办 新西兰预科成绩不合格怎么办 私立转公立学籍怎么办 法国留学拒签怎么办 法国留学签证被拒怎么办 英国留学拒签怎么办 在普高出国怎么办 副高职称不让出国怎么办 没学位现场确认怎么办 香港master挂科怎么办 香港研究生挂科怎么办 加拿大研究生绩点刚合格怎么办 l2续签失败 学校怎么办 珍珠柏盆景黄叶怎么办 导师中途辞职了怎么办