Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7278891
  • 博文数量: 512
  • 博客积分: 12019
  • 博客等级: 上将
  • 技术积分: 6857
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-01 16:46
文章分类

全部博文(512)

文章存档

2024年(2)

2022年(2)

2021年(6)

2020年(59)

2019年(4)

2018年(10)

2017年(5)

2016年(2)

2015年(4)

2014年(4)

2013年(16)

2012年(47)

2011年(65)

2010年(46)

2009年(34)

2008年(52)

2007年(52)

2006年(80)

2005年(22)

分类: C/C++

2007-11-14 11:38:54

C++回调函数(callback)与仿函数(functor)的异同

许式伟 (版权声明)
2007-3-3

回调函数(callback)与仿函数(functor)很多时候从用途上来看很相似,以致于我们经常将它们相提并论。例如:

inline bool compare(int a, int b)
{
   
return a > b;
}
 
struct comparer {
  
bool operator()(int a, int b) const {
     
return a > b;
  }
};
 
void main()
{
   std::vector
<int> vec, vec2;
   std::sort(vec.begin(), vec.end(), compare);
   std::sort(vec2.begin(), vec2.end(), comparer());
}

仿函数(functor)之所以称为仿函数,是因为这是一种利用某些类对象支持operator()的特性,来达到模拟函数调用效果的技术。

如果这里vec, vec2这两个vector的内容一样,那么从执行结果看,使用回调函数compare与使用仿函数comparer是一样的。

那么,我们应该用回调,还是用仿函数?

很多人都说用仿函数吧,回调函数是丑陋的,代码不太象C++风格。

但其实问题的本质不是在代码风格上,仿函数与回调函数各有利弊,不能一概而论。

仿函数(functor)的优点

我的建议是,如果可以用仿函数实现,那么你应该用仿函数,而不要用回调。原因在于: 

  • 仿函数可以不带痕迹地传递上下文参数。而回调技术通常使用一个额外的void*参数传递。这也是多数人认为回调技术丑陋的原因。
  • 更好的性能。

仿 函数技术可以获得更好的性能,这点直观来讲比较难以理解。你可能说,回调函数申明为inline了,怎么会性能比仿函数差?我们这里来分析下。我们假设某 个函数func(例如上面的std::sort)调用中传递了一个回调函数(如上面的compare),那么可以分为两种情况:

  • func是内联函数,并且比较简单,func调用最终被展开了,那么其中对回调函数的调用也成为一普通函数调用(而不是通过函数指针的间接调用),并且如果这个回调函数如果简单,那么也可能同时被展开。在这种情形下,回调函数与仿函数性能相同。
  • func 是非内联函数,或者比较复杂而无法展开(例如上面的std::sort,我们知道它是快速排序,函数因为存在递归而无法展开)。此时回调函数作为一个函数 指针传入,其代码亦无法展开。而仿函数则不同。虽然func本身复杂不能展开,但是func函数中对仿函数的调用是编译器编译期间就可以确定并进行 inline展开的。因此在这种情形下,仿函数比之于回调函数,有着更好的性能。并且,这种性能优势有时是一种无可比拟的优势(对于std::sort就 是如此,因为元素比较的次数非常巨大,是否可以进行内联展开导致了一种雪崩效应)。

仿函数(functor)不能做的?

话又说回来了,仿函数并不能完全取代回调函数所有的应用场合。例如,我在std::AutoFreeAlloc中使用了回调函数,而不是仿函数,这是因为AutoFreeAlloc要容纳异质的析构函数,而不是只支持某一种类的析构。这和模板(template)不能处理在同一个容器中支持异质类型,是一个道理。

 
阅读(1727) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~