我看到函数对象经常与 STL 算法一起使用。函数对象是因为这些算法而出现的吗?什么时候在 C++ 中使用函数对象?它有什么好处?
7 回答
正如 jdv 所说,使用函子而不是函数指针,这对于编译器来说更难优化和内联;此外,仿函数的一个基本优点是它们可以轻松地在调用它们1之间保持状态,因此它们可以根据调用它们的其他时间以不同的方式工作,以某种方式跟踪它们使用的参数,...
例如,如果您想将两个整数容器中的所有元素相加,您可以执行以下操作:
struct
{
int sum;
void operator()(int element) { sum+=element; }
} functor;
functor.sum=0;
functor = std::for_each(your_first_container.begin(), your_first_container.end(), functor);
functor = std::for_each(your_second_container.begin(), your_second_container.end(), functor);
std::cout<<"The sum of all the elements is: "<<functor.sum<<std::endl;
- 实际上,正如 R Samuel Klatchko 在下面指出的那样,它们可以支持多个独立状态,每个 functor 实例一个:
更准确一点的说法是仿函数可以支持多个独立状态(函数可以通过既不是线程安全也不是可重入的静态/全局变量支持单个状态)。
函子使您能够使用更复杂的状态,例如共享状态(静态字段)和私有状态(实例字段)。然而,这种进一步的灵活性很少使用。
通常使用函数对象(函子)代替函数指针。函数指针的问题是编译器通常将它们作为原始指针传递,这使得编译器以后很难内联代码。而且它们更容易提供参数。
函数对象被设计为允许对 STL 进行强大的抽象层,在这方面它们很棒。
但是,我更喜欢使用boost::bind
函数并将其绑定到 STL 算法——通常(尽管不是在对象具有状态的情况下)这似乎是一个更优雅的解决方案。
std::for_each( callback.begin(), callback.end(),
boost::bind(&Callback::call(),_1)
);
此外,另一个即将到来的替代方案是 C++0x 中的 lambda(从 Wikipedia 无耻地窃取的示例):
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&total](int x) {
total += x;
});
std::cout << total;
请注意,由于闭包,它们没有绑定关于没有状态的限制。
函数对象是一个同时也是对象的函数,即它具有状态。普通函数一般没有状态。它们可以通过访问全局变量来模拟具有状态,但随后状态在所有调用中共享。
我不能说他们为什么会出现——可能只是因为他们可以!
什么时候使用函子?考虑到仿函数只是将您通常放入循环中的代码移动到类的 operator() 中,它们与仅在 while 循环中调用函数没有太大区别……除了使用它们你允许编译器内联代码,你也可以传递一个预先构造的对象,你已经构造了一些状态。后一点使它们非常强大。
将排序算法与 CRT 的 qsort 调用进行比较。他们做同样的事情,只是做的完全不同。
将函数封装为对象的想法可以追溯到 Lisp 和 Smalltalk。函子的 C++ 思想是 Jim Coplien 在 1991 年出版的Advanced C++ Programming Styles and Idioms一书中的一个章节。STL使用了这个成语并进一步普及了它。
本文对函数对象进行了深入的研究,以及它如何使代码更强大、更简洁。