函数对象/仿函数

来源:互联网 发布:简单的sql注入 编辑:程序博客网 时间:2024/06/05 16:07

本文内容全部转载  http://www.cnblogs.com/weiqubo/archive/2011/02/16/1956552.html

                              https://my.oschina.net/u/178323/blog/33374  

       http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html   

感谢他们!!


函数对象/仿函数

提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的基本算法,迭代器是由容器提供的一种接口,算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件,函数对象(Function Object,JJHou译作Functor仿函数)。

什么是函数对象

  顾名思义,函数对象首先是一个对象,即某个类的实例。其次,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,如参数传递、返回值等。这种行为是通过重载类的()操作符来实现的,举例说明之,

篇二、函数对象


      前面是函数指针的应用,从一般的函数回调意义上来说,函数对象和函数指针是相同的,但是函数对象却具有许多函数指针不具有的特点,函数对象使程序设计更加灵活,而且能够实现函数的内联(inline)调用,使整个程序实现性能加速。


函数对象:这里已经说明了这是一个对象,而且实际上只是这个对象具有的函数的某些功能,我们才称之为函数对象,意义很贴切,如果一个对象具有了某个函数的功能,我们变可以称之为函数对象。
如何使对象具有函数功能呢,很简单,只需要为这个对象的操作符()进行重载就可以了,如下:

class A{public:int operator()(int x){return x;}};A a;a(5);

这样a就成为一个函数对象,当我们执行a(5)时,实际上就是利用了重载符号()。

函数对象既然是一个“类对象”,那么我们当然可以在函数形参列表中调用它,它完全可以取代函数指针!如果说指针是C的标志,类是C++特有的,那么我们也可以说函数指针和函数对象之间的关系也是同前者一样的!(虽然有些严密)。当我们想在形参列表中调用某个函数时,可以先声明一个具有这种函数功能的函数对象,然后在形参中使用这个对象,他所作的功能和函数指针所作的功能是相同的,而且更加安全。
下面是一个例子:
复制代码
class Func{public: int operator() (int a, int b) { cout<<a<<'+'<<b<<'='<<a+b<<endl; return a; }};int addFunc(int a, int b, Func& func){ func(a,b); return a;}Func func;addFunc(1,3,func);
复制代码

上述例子中首先定义了一个函数对象类,并重载了()操作符,目的是使前两个参数相加并输出,然后在addFunc中的形参列表中使用这个类对象,从而实现两数相加的功能。
如果运用泛型思维来考虑,可以定一个函数模板类,来实现一般类型的数据的相加:
复制代码
class FuncT{public: template<typename T> T operator() (T t1, T t2) { cout<<t1<<'+'<<t2<<'='<<t1+t2<<endl; return t1; }};template <typename T>T addFuncT(T t1, T t2, FuncT& funct){ funct(t1,t2); return t1;}FuncT funct;addFuncT(2,4,funct);addFuncT(1.4,2.3,funct);
复制代码

//****************************//

函数对象实质上是一个实现了operator()--括号操作符重载--的类。它与函数指针用法一样,但是它有一个优点,函数指针不可以传递附加数据过去,但是在函数对象中,我们可以传递附加数据过去。

 

先讲解下运算符重载吧,对于运算符函数我们有两种定义方式,

1,如果此函数是属于一个类的成员函数,那么我们是这么定义的:参考<< ,如下:

ostream & operator<<(const string & str);

我们可以通过这样的形式来调用:cout << str ;

注意:按正常情况下,对于一个类A中的函数,我们应该这样调用此类中的成员函数:A.func() ,但是对于运算符重载函数,我们并不是这样,如同cout << str ,cout就是调用<<运算符的一个对象,这里没有点号了。

2,如果此函数并非另一个类中的成员函数,而是一个普通函数,那么我们应该这么定义:

type operator(a, b); type是返回类型

我们可以这样使用:a operator b;

 

所以对于重载了()运算符的函数对象类,如果通过函数对象类定义了一个对象a,那么我们这样使用对象a的“()”运算符重载:

a();

对,就这样,表明此“()”是对象a中的成员函数

如果要传递参数的话,可以这样:a(type name, ......);  name就是传递到“()”重载函数中的参数

 

下面看两个例子:

第一个是演示函数对象怎么使用的:

#include <iostream>#include <string>using namespace std;class funObject {//定义一个函数对象public:    funObject(const char *name) : str(name)//构造函数,在构造函数中传递一个参数过来     {        cout << str << endl;    }    void operator()(char *name) {//重载()运算符       if (!str.empty())            cout << str;//打印函数对象中私有的数据       cout << name << endl;//打印()函数中传递过来的实参    }private:    string str;};int main(){    //funObject("hello");//仅仅调用了一下函数对象的构造函数    funObject obj("hello");//定义了一个函数对象类的一个对象,并传递了一个附加数据    obj(" kitty");调用obj对象的()函数,实参是"kitty".    return 0;}

运行结果:[chengyang@localhost functionObject]$ ./test2hellohello kitty

函数对象经常用于C++标准算法中,作为一个操作函数参数传递

 

另外,函数对象还有一个函数指针无法匹敌的用法:可以用来封装类成员函数指针!


因为函数对象可以携带附加数据,而成员函数指针缺少一个类实体(类实例)指针来调用,因此,可以把类实体指针给函数对象保存起来,就可以用于调用对应类实体成员函数了。

看如下代码:

#include <iostream>using namespace std;class A {public:    void print(const char *name) //一个类中的函数,我们要通过函数对象将其封装    {cout << "hello " << name << "!" << endl;}};template<typename T>class funObject {//函数对象public:    funObject(void(T::*f)(const char *), T *obj) : pFunc(f), pObj(obj){}//构造函数,传递附加数据    void operator()(const char *name)//通过此函数封装某一个类的函数    {      (pObj->*pFunc)(name);//调用某一个类的一个对象的一个函数成员,这就是封装    }private:    void(T::*pFunc)(const char *);//储存某一个类中的函数    T *pObj;//储存某一个类的一个对象};int main(){    A a;    funObject<A> call(&A::print, &a);//定义一个函数对象类的对象,传递一个类的对象和一个类的某个成员函数进去    call("kitty");//调用封装对象某个对象的()函数,在此函数中,我们又进而通过某一个类的一个对象调用此类的一个成员函数    return 0;}运算结果如下:[chengyang@localhost functionObject]$ ./test1hello kitty!




//*********************//

篇一、函数指针
函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址。
函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数。
函数指针的声明方法
数据类型标志符 (指针变量名) (形参列表);
一般函数的声明为: 
int func ( int x );
而一个函数指针的声明方法为:
int (*func) (int x);
前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针的函数,后面的形参要视这个函数指针所指向的函数形参而定。
然而这样声明我们有时觉得非常繁琐,于是typedef可以派上用场了,我们也可以这样声明:
typedef int (*PF) (int x);
PF pf;
这样pf便是一个函数指针,方便了许多。当要使用函数指针来调用函数时,func(x)或者  (*fucn)(x) 就可以了,当然,函数指针也可以指向被重载的函数,编译器会为我们区分这些重载的函数从而使函数指针指向正确的函数。
例子:

复制代码
typedef void (*PFT) ( char ,int );void bar(char ch, int i){ cout<<"bar "<<ch<<' '<<i<<endl; return ;}PFT pft;pft = bar;pft('e',91);
复制代码

例子中函数指针pft指向了一个已经声明的函数bar(),然后通过pft来实现输出字符和整型的目的。
函数指针另一个作用便是作为函数的参数,我们可以在一个函数的形参列表中传入一个函数指针,然后便可以在这个函数中使用这个函数指针所指向的函数,这样便可以使程序变得更加清晰和简洁,而且这种用途技巧可以帮助我们解决很多棘手的问题,使用很小的代价就可获得足够大的利益(速度+复杂度)。
复制代码
typedef void (*PFT) ( char ,int );void bar(char ch, int i){ cout<<"bar "<<ch<<' '<<i<<endl; return ;}void foo(char ch, int i, PFT pf){ pf(ch,i); return ;}PFT pft;pft = bar;foo('e',12,pft);
复制代码

上述例子我们首先利用一个函数指针pft指向bar(),然后在foo()函数中使用pft指针来调用bar(),实现目的。将这个特点稍加利用,我们就可以构造出强大的程序,只需要同样的foo函数便可以实现对不同bar函数的调用。

篇二、函数对象
前面是函数指针的应用,从一般的函数回调意义上来说,函数对象和函数指针是相同的,但是函数对象却具有许多函数指针不具有的有点,函数对象使程序设计更加灵活,而且能够实现函数的内联(inline)调用,使整个程序实现性能加速。
函数对象:这里已经说明了这是一个对象,而且实际上只是这个对象具有的函数的某些功能,我们才称之为函数对象,意义很贴切,如果一个对象具有了某个函数的功能,我们变可以称之为函数对象。
如何使对象具有函数功能呢,很简单,只需要为这个对象的操作符()进行重载就可以了,如下:
class A{public:int operator()(int x){return x;}};A a;a(5);

这样a就成为一个函数对象,当我们执行a(5)时,实际上就是利用了重载符号()。
函数对象既然是一个“类对象”,那么我们当然可以在函数形参列表中调用它,它完全可以取代函数指针!如果说指针是C的标志,类是C++特有的,那么我们也可以说指针函数和函数对象之间的关系也是同前者一样的!(虽然有些严密)。当我们想在形参列表中调用某个函数时,可以先声明一个具有这种函数功能的函数对象,然后在形参中使用这个对象,他所作的功能和函数指针所作的功能是相同的,而且更加安全。
下面是一个例子:
复制代码
class Func{public: int operator() (int a, int b) { cout<<a<<'+'<<b<<'='<<a+b<<endl; return a; }};int addFunc(int a, int b, Func& func){ func(a,b); return a;}Func func;addFunc(1,3,func);
复制代码

上述例子中首先定义了一个函数对象类,并重载了()操作符,目的是使前两个参数相加并输出,然后在addFunc中的形参列表中使用这个类对象,从而实现两数相加的功能。
如果运用泛型思维来考虑,可以定一个函数模板类,来实现一般类型的数据的相加:
复制代码
class FuncT{public: template<typename T> T operator() (T t1, T t2) { cout<<t1<<'+'<<t2<<'='<<t1+t2<<endl; return t1; }};template <typename T>T addFuncT(T t1, T t2, FuncT& funct){ funct(t1,t2); return t1;}FuncT funct;addFuncT(2,4,funct);addFuncT(1.4,2.3,funct);
复制代码

大名鼎鼎的STL中便广泛的运用了这项技术,详细内容可参见候捷大师的一些泛型技术的书籍,不要以为函数对象的频繁调用会使程序性能大大折扣,大量事实和实验证明,正确使用函数对象的程序要比其他程序性能快很多!所以掌握并熟练运用函数对象才能为我们的程序加分,否则.......
如此看来,函数对象又为C++敞开了一道天窗,但随之而来的便是一些复杂的问题和陷阱,如何去蔽扬利还需要我们不断学习和探索。

0 0