C++中的函数对象(一)
STL中的函数对象,大多数STL类属算法(以及某些容器类)可以以一个函数对象作为参数。引入函数对象的目的是为了使算法的功能富于变化,从而增强算法的通用性。
所谓函数对象,是指一段代码实体,它可以不带参数,也可以带有若干参数,其功能是获得一个值,或者改变操作的状态。在C++编程中,任何普通的函数都满足这个定义,而且,任何一个重载了运算符operator()的类的对象也都满足这一定义,称为函数对象。
普通函数
int multfun(int x, int y) { return x*y; }
或者下面所定义的multiply类型的对象multfunobj
class multiply{ public: int operator()(int x, int y) const {return x*y;} };
multiply multfunobj;
上面的两种方式,都可以用下面的方式调用:
int product1 = multfun(3, 7);
int product2 = multfunobj(3, 7);
1.通过函数指针传递函数参数
#include <iostream> #include <cassert> #include <vector> using namespace std; int multfun(int x, int y) { return x*y; } template <typename InputIterator, typename T> T accumulate1(InputIterator first, InputIterator last, T init, T(*binary_function)(T x, T y)) { while(first != last) { init = (*binary_function)(init, *first); ++first; } return init; } int main() { cout << "Demonstrating function pointer passing." << endl; int x[5] = {2, 3, 5, 7, 11}; vector<int> vector1(&x[0], &x[5]); int product = accumulate1(vector1.begin(), vector1.end(), 1, &multfun); assert (product == 2310); cout << "---- OK."<<endl; return 0; }
输出--- OK
这种传统实现方式的一个基本问题是其通用性不够强,例如,函数指针原型可以写成
T (*binary_function)(T x, T y);
并且可以和int multifun(int ,int)匹配,但是,如果写成 T (*binary_function)(const T& x, const T& y);以便使其在T类对象比较大的情况下具有较高效率,则multifun将无法与之匹配,此时必须以原型 int multifun(const int&, cosnt int& )对函数进行重写。
另外个问题,在于效率上的,在accumulate1中,必须通过一个指针才能访问以参数形式传递进来的函数,而且只能外联(out of line)地调用该函数(首先向函数传递参数,然后把控制权移交给函数,并且把返回值拷贝到调用域中,最后把控制移交给函数的调用域)。在被调用函数为mutifun的情况下,函数内部的计算只需要一两个机器指令,而这些步骤的开销却要大的多。
2.通过模板参数定义函数对象的优越性
#include <iostream> #include <cassert> #include <vector> using namespace std; template <typename InputIterator, typename T, typename BinaryFunction> T accumulate (InputIterator first, InputIterator last, T init, BinaryFunction binary_function) { while(first != last) { init = binary_function(init, *first); ++first; } return init; } class multiply{ public: int operator()(int x, int y) const {return x*y;} }; multiply multfunobj; int main() { cout << "Demonstrating function pointer passing." << endl; int x[5] = {2, 3, 5, 7, 11}; vector<int> vector1(&x[0], &x[5]); int product = accumulate(vector1.begin(), vector1.end(), 1, multfunobj); assert(product == 2310); cout << "------ OK"<< endl; return 0; }
accumulate的这种定义下,该函数的最后一个实际参数只需要满足:带两个参数,其类型分别为T1和T2,其中类型T可以转换为T1,inputIterator的值类型可以转换为T2,
且该参数的返回值类型可以转换为T,这样一来,与函数指针传递的过于严格的类型要求相比,这样的定义方式可以接受更多种类的函数原型。
在性能方面,由于通过模板参数和重载operator()定义了可以作为参数传递的函数对象,因此编译器可以把binary_function函数调用内联(inline)在accumulate函数体中,
从而彻底消除了指针解析和外联调用所带来的额外开销。
此外,类定义中还可以定义一些额外的信息,这样类的对象就携带了一些额外的信息。
接下来我们就对类对象的一些基本用法进行简单举例。
参考《标准模板库自修教程与参考手册——STL进行C++编程》