首页 > 代码库 > 从for_each开始说起 回调函数与仿函数

从for_each开始说起 回调函数与仿函数

#include <iostream>
#include <algorithm>
using namespace std;
//回调函数
void call_back(char elem)
{
 cout << elem << endl;
}
//仿函数
struct Functor
{
 void operator() (char elem) 
 {
  cout << elem << endl;
 } 
};
int main()
{
 string strA = "hello";
 string strB = "world";
 
 for_each(strA.begin(),strA.end(),Functor());
 cout<<"===========GAP==============="<<endl;
 for_each(strB.begin(),strB.end(),call_back);
 getchar();
 return 0;
}

h
e
l
l
o
===========GAP===============
w
o
r
l
d

可能会有疑问两者有什么区别?

假如我要for_each遍历的不是字符串而是int类型的vector呢?

是不是又要重写一个int类型作为参数的回调函数,那如果有N种类型的容器遍历岂不是要写N个回调函数或N个仿函数类?

非也!!!

C++有类模板 也有 函数模板 同样可以用于回调

?#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//模板函数
template<typename T>
void call_back(T elem)
{ 
 cout<< elem <<endl;
}
//仿函数
template<typename T>
class Functor
{
public:
 Functor()
  :m_val(0)
 {
  cout<< "Functor()" <<endl;
 }
 ~Functor()
 {
  cout<<"~Functor()"<<endl;
 }
 void operator() (T elem) 
 {
  Do(elem);
 }
 
 //举个栗子
 void Do(T elem)
 {
  m_val+=elem;
  cout<<elem<<"/"<<m_val<<endl;
 }
private:
 T m_val;
};
 
int main()
{
 vector<int> vec;
 vec.push_back(1);
 vec.push_back(2);
 vec.push_back(3);
 vec.push_back(4);
 vec.push_back(5);
 for_each(vec.begin(),vec.end(),call_back<int>);
 cout<<"===========GAP==============="<<endl; 
 for_each(vec.begin(),vec.end(),Functor<int>());
 return 0;
}

1
2
3
4
5
===========GAP===============
Functor()
1/1
2/3
3/6
4/10
5/15
~Functor()
~Functor()
~Functor()

 

三次析构的原因:

先附上for_each的源码(VC2008)

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);
}
Functor<int>() 产生临时对象传参(值) 构造一次,析构一次 
for_each参数值传递,拷贝构造一次,析构一次(函数内部)
for_each返回仿函数的对象(值),拷贝构造一次,析构一次
因为没有重载拷贝构造函数 所以打印出第一次创建临时对象时的普通构造函数
实际上在这个过程中一共产生过三个仿函数对象

 

如果把代码改变下:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

template<typename T>
class Functor
{
public:
 Functor()
  :m_val(0)
 {
  cout<< "Functor()"<<this<<endl;
 }
 Functor(Functor& that)
 {
  this->m_val = that.m_val;
  cout<< "Copy Functor()" <<this<<endl;
 }
 ~Functor()
 {
  cout<<"~Functor()"<<this<<endl;
 }
 void operator() (T elem) 
 {
  Do(elem);
 }
 
 //举个栗子
 void Do(T elem)
 {
  m_val+=elem;
  cout<<elem<<"/"<<m_val<<endl;
 }
 T getVal()
 {
  return m_val;
 }
private:
 T m_val;
};
 
int main()
{
 vector<int> vec;
 vec.push_back(1);
 vec.push_back(2);
 vec.push_back(3);
 vec.push_back(4);
 vec.push_back(5);

 Functor<int> func;
 Functor<int>& ref = for_each(vec.begin(),vec.end(),func);
 cout<<ref.getVal()<<endl;
 
 return 0;
}

运行结果

Functor()0032F800           //main函数中的实参仿函数对象
Copy Functor()0032F68C  //值传递 对【实参对象】拷贝构造了形参对象

1/1
2/3
3/6
4/10
5/15
Copy Functor()0032F7E8 //返回对象的值类型  对【形参对象】拷贝构造

~Functor()0032F68C       //析构形参对象
15
~Functor()0032F7E8       //析构返回值对象
~Functor()0032F800      //析构实参对象

现在一目了然了吧!

 

 个人认为使用回调函数高效 由上面的例子可以看出 构造1次 拷贝构造2次 析构3次  是有代价的

 

最后回到仿函数和回调函数

区别在于:

  1. 使用仿函数可以声明在业务相关的类内部 缩小作用域

  2. 使用仿函数可以使用类的成员属性和成员函数

  3. 仿函数是一个 可以使用面向对象的各种机制(封装 继承 多态)

  4. 若使用回调函数 那么只能声明为某个类的静态成员函数或全局函数,使用类内部的资源需要用一些手段传参,没有直接使用成员函数便捷