STL中仿函数(functors)、类成员和mem_fun的使用

仿函数(functor): 重载了"()"运算符的struct,例如:

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

那看看源码是怎么回事,在SGI STL的stl_function.h:

template <class _Ret, class _Tp>
inline mem_fun_t<_Ret,_Tp> mem_fun(_Ret (_Tp::*__f)())
{ return mem_fun_t<_Ret,_Tp>(__f); }

原来mem_fun返回的是一个对象:mem_fun_t<_Ret,_Tp>.(不要嫌人家命名太怪异).那mem_fun_t<_Ret,_Tp>又是什么东东?还是看源码:

template <class _Ret, class _Tp>
class mem_fun_t : public unary_function<_Tp*,_Ret> {
public:
    explicit mem_fun_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {}
    _Ret operator()(_Tp* __p) const { return (__p->*_M_f)(); }
private:
    _Ret (_Tp::*_M_f)();
};

原来mem_fun_t就是一个functor,这下就满足了for_each的要求了。其调用流程是这样的,for_each把vector中的元素传送给mem_fun,mem_fun自己产生一个仿函数mem_fun_t,然后仿函数调用其重载的()。过程就这么简单。当然你不能对其他类的成员函数进行绑定,因为在for_each调用过程中,会传递其*iterator值,如果是其他类的成员函数,那么这个类的对象无法传入,当然就无法完成任务了。

这里使用的是vector<D*> V; 在mem_fun_t构造函数中,刚好需要指针,如果不是D*, 而是使用vector<D> V; 还能用吗?

这是你需要使用的是mem_fun_ref。把程序改成:

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(D(1));
  V.push_back( D(2));
  V.push_back( D());
  V.push_back( D(3));

  for_each(V.begin(), V.end(), mem_fun_ref(&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));
}

mem_fun1和mem_fun1_ref,他们可以使用绑定一个参数的类成员。

 

ContractedBlock.gif ExpandedBlockStart.gif Code
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));
 }

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

测试代码如下:

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

using namespace std;

class Book
{
    
public:
        
string m_strName;

        Book(
string name) : m_strName(name)
        {

        }

        
void SayHello()
        {
            cout 
<< "Hello " << m_strName << endl;
        }
};

class CBookEditDlg
{
public:
    vector
<Book> m_vecBooks;

    
void ForEachBookFunctor(Book book);

    
void PubBookInfo();
};

void CBookEditDlg::ForEachBookFunctor(Book book)
{
    cout 
<< book.m_strName << endl;
}

void CBookEditDlg::PubBookInfo()
{

    
//下面这两句起的作用是一样的,至于为什么一样很值得仔细揣摩揣摩的

    for_each(m_vecBooks.begin(), m_vecBooks.end(), mem_fun_ref(
&Book::SayHello));
    for_each(m_vecBooks.begin(), m_vecBooks.end(), std::bind1st(mem_fun(
&CBookEditDlg::ForEachBookFunctor), this));
}

int main()
{
    Book book1(
"Effective C++");
    Book book2(
"More Effective C++");

    CBookEditDlg books;

    books.m_vecBooks.push_back(book1);
    books.m_vecBooks.push_back(book2);

    books.PubBookInfo();
   
    
return 0;
}

先理一下关系吧,bind1st返回一个binder1st对象,而binder1st构造函数需要接受两个参数,分别为mem_fun_t或者是mem_fun1_t对象和对象this指针,mem_fun_t和mem_fun1_t则用一个函数指针保存有对象成员方法的地址。既然这里用到了mem_fun1_t,我们就来分析它的源代码。

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

 _Result 
operator()(_Ty *_Pleft, _Arg _Right) const
  { 
// call function with operand
  return ((_Pleft->*_Pmemfun)(_Right));
  }

private:
 _Result (_Ty::
*_Pmemfun)(_Arg); // the member function pointer
 };

 

上面的测试代码中,binder1st接受的_Func类型为std::mem_fun1_t
<void,CBookEditDlg,Book>,这个可以通Local窗口看到的,其父类binary_function为std::binary_function<CBookEditDlg *,Book,void>,这样可以很清楚看到_Result对应void,_Ty对应CBookEditDlg,_Arg对应Book,进一步也就是说binary_function三个typedef的类型中,first_argument_type为CBookEditDlg *,second_argument_type为Book,result_type为void,可以看出这里必须使用bind1st绑定器将this绑定在第一个参数上,bind2nd同bind1st是非常类似的,如果这里误用了bind2nd将通不过编译(最后面我们给出一个例子看看bind1st和bind2nd都可以用的情况)。我们现在可以很清楚理解mem_fun1_t了,它里面有一个函数指针,定义为"void (CBookEditDlg::*_Pmemfun)(Book)"。mem_fun1_t重载了函数运算符,它接受两个参数,分别为对象的this指针和参数_Right。我现在真的是非常佩服C++编译器的能力,bind1st,mem_fun我都没有给出实例化参数,它能从提供的参数自动提取出型别来实例化模板。例子程序中它就知道使用那个一个mem_fun模板,我之前一直以为它会返回一个mem_fun_t对象,然后就怎么都想不通了。

ok,接下来我们看binder1st的代码。

  
// TEMPLATE CLASS binder1st
template<class _Fn2>
 
class binder1st
  : 
public unary_function<typename _Fn2::second_argument_type,
   typename _Fn2::result_type
>
 { 
// functor adapter _Func(stored, right)
public:
 typedef unary_function
<typename _Fn2::second_argument_type,
  typename _Fn2::result_type
> _Base;
 typedef typename _Base::argument_type argument_type;
 typedef typename _Base::result_type result_type;

 binder1st(
const _Fn2& _Func,
  
const typename _Fn2::first_argument_type& _Left)
  : op(_Func), value(_Left)
  { 
// construct from functor and left operand
  }

 result_type 
operator()(const argument_type& _Right) const
  { 
// apply functor to operands
  return (op(value, _Right));
  }

 result_type 
operator()(argument_type& _Right) const
  { 
// apply functor to operands
  return (op(value, _Right));
  }

protected:
 _Fn2 op; 
// the functor to apply
 typename _Fn2::first_argument_type value; // the left operand
 };

binder1st有一个op保存之前的那个mem_fun1_t对象,然后它重载了函数运算符,接受两个参数,一个是value,一个是_Right,value是CBookEditDlg 
*类型的,它就是bind1st接受的this参数了,_Right则是Book的对象引用。这里之所以是引用是因为,for_each传递给它后面函数对象并不是迭代器,而是*迭代器,在例子程序中就是Book对象了。有人可能认为例子程序中ForEachBookFunctor函数如果接受Book引用效率会更高一些,但是如果真将"Book book"改成"Book& book"会导致通不过编译,这是因为binary_function的second_argument_type就变成了"Book&"了,这个时候"result_type operator()(argument_type& _Right) const"就出现了对引用的引用了,所以通不过编译。我想STL大量的拷贝是降低其效率的一个重要因素。分析binder1st我们就能明白binary_function所取到的重要作用,有了它binder1st重载函数运算符的那个函数才知道自己应该接受什么类型的参数,返回什么类型的参数。

op(value, _Right)会调用下面这个函数(mem_fun1_t重载的):

 _Result 
operator()(_Ty *_Pleft, _Arg _Right) const
  { 
// call function with operand
  return ((_Pleft->*_Pmemfun)(_Right));
  }

最后附上一段有趣的代码(引自http:
//cpp-circle.group.javaeye.com/group/blog/336258,这里都只是讲一元函数和二元函数而已,如果这个sum做三个参数之和,我想绑定器就要傻眼了)

#include 
<functional>
#include 
<iostream>

using namespace std;   
  
int sum(int arg1, int arg2)   
{   
    cout
<<"ARG 1: "<<arg1<<endl;   
    cout
<<"ARG 2: "<<arg2<<endl;   
  
    
int sum = arg1 + arg2;   
    cout
<<"SUM: "<<sum<<endl;   
  
    
return sum;   
}   
  
int main(int argc,char* argv[])   
{   
    bind1st(ptr_fun(sum),
1)(2); // the same as sum(1,2)   

 cout 
<< "===========================" << endl;

    bind2nd(ptr_fun(sum),
1)(2); //the same as sum(2,1)   
 
    
return 0;   
}  

 

转载于:https://www.cnblogs.com/dubingsky/archive/2009/06/21/1507800.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值