相比
double average = CalculateAverage(values.begin(), values.end());
Run Code Online (Sandbox Code Playgroud)
同
double average = std::for_each(values.begin(), values.end(), CalculateAverage());
Run Code Online (Sandbox Code Playgroud)
使用函子而不是函数有什么好处?是不是第一个更容易阅读(甚至在添加实施之前)?
假设仿函数定义如下:
class CalculateAverage
{
private:
std::size_t num;
double sum;
public:
CalculateAverage() : num (0) , sum (0)
{
}
void operator () (double elem)
{
num++;
sum += elem;
}
operator double() const
{
return sum / num;
}
};
Run Code Online (Sandbox Code Playgroud)
Oli*_*rth 75
至少有四个很好的理由:
关注点分离
在您的特定示例中,基于仿函数的方法具有将迭代逻辑与平均计算逻辑分离的优点.因此,您可以在其他情况下使用您的仿函数(考虑STL中的所有其他算法),您可以使用其他仿函数for_each.
参数化
您可以更轻松地参数化仿函数.因此,例如,您可以使用一个CalculateAverageOfPowers仿函数来获取数据的平方或立方体等的平均值,这样就可以编写:
class CalculateAverageOfPowers
{
public:
CalculateAverageOfPowers(float p) : acc(0), n(0), p(p) {}
void operator() (float x) { acc += pow(x, p); n++; }
float getAverage() const { return acc / n; }
private:
float acc;
int n;
float p;
};
Run Code Online (Sandbox Code Playgroud)
你当然可以用传统的函数做同样的事情,但是后来很难使用函数指针,因为它有一个不同的原型CalculateAverage.
有状态
仿函数可以是有状态的,你可以这样做:
CalculateAverage avg;
avg = std::for_each(dataA.begin(), dataA.end(), avg);
avg = std::for_each(dataB.begin(), dataB.end(), avg);
avg = std::for_each(dataC.begin(), dataC.end(), avg);
Run Code Online (Sandbox Code Playgroud)
平均多个不同的数据集.
请注意,几乎所有接受仿函数的STL算法/容器都要求它们是"纯"谓词,即状态随时间没有可观察到的变化. for_each在这方面是一个特例(参见例如Effective Standard C++ Library - for_each vs. transform).
性能
编译器通常可以内联函数(毕竟,STL是一堆模板).虽然理论上函数也是如此,但编译器通常不会内联函数指针.典型的例子是比较std::sortvs qsort; 假设比较谓词本身很简单,STL版本通常快5-10倍.
摘要
当然,可以使用传统的函数和指针来模拟前三个,但是使用仿函数会变得更加简单.
函子的优点:
std::for_each很容易是标准算法中最反复无常且最不实用的.它只是一个很好的循环包装器.然而,即使它有优势.
考虑您的第一个版本CalculateAverage必须是什么样子.它将在迭代器上有一个循环,然后对每个元素进行处理.如果你错误地写了这个循环怎么办?哎呀; 有编译器或运行时错误.第二个版本永远不会有这样的错误.是的,这不是很多代码,但为什么我们必须经常编写循环?为什么不一次?
现在,考虑真正的算法; 实际上有效的.你想写std::sort吗?还是std::find?还是std::nth_element?你甚至知道如何以最有效的方式实现它吗?您想要实施这些复杂算法多少次?
至于阅读的方便性,这是在旁观者的眼中.正如我所说,std::for_each它几乎不是算法的首选(尤其是C++ 0x基于范围的语法).但是,如果你在谈论真正的算法,它们是非常可读的; std::sort对列表进行排序.一些比较模糊的东西std::nth_element不会那么熟悉,但你总能在你方便的C++参考中查找它.
一旦你在C++ 0x中使用Lambda,甚至std :: for_each都是完全可读的.