首页 > 代码库 > 浅析STL 谓词 + 仿函数 + 函数指针(c)

浅析STL 谓词 + 仿函数 + 函数指针(c)

一:起因

(0)提到C++ STL,首先被人想到的是它的三大组件:Containers(容器), Iterators(迭代器), Algorithms(算法)。容器为用户提供了常用的数据结构(如,vector,list,deque,stack,map,multimap,set,multiset,外加string),算法大多是独立于容器的常用的基本算法(一般在algorithm头文件中,其中sort比较常用),迭代器是由容器提供的一种接口,算法通过迭代器来操控容器。详情请看 博客

(1)接下来要介绍的是另外的一种组件,函数对象(Function Object,JJHou译作Functor仿函数)

(2)定义:仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了;  函数对象(仿函数)——顾名思义,函数对象首先是一个对象,即某个类的实例。其次,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,这种行为是通过重载类的()操作符来实现的,举例说明:

class FunctionObjectType
{
     public:
         void operator()()
		 {
            statements
         }
}
(3)其实sort(v.begin(), v.end())时(假定v是vector<int>),调用的是sort(v.begin(), v.end(), less<int>()),若要逆向排序,用函数对象greater<int>(). less<T>和

greater<T>是STL中的两个模板类,它们使用类型T的<和>操作符。less<T>的一个典型实现可能是这样的:


template <class T>
class less
{
	public:
		bool operator()(const T& l, const T& r) const
		{
			return l < r;
		}
};

int main(int argc, char **argv)
{
    Print print;
    print(372);
    print.operator()(372); //~ 显式调用
    return 0;
}
(4) 仿函数的由来:在我们写代码时有时会发现有些功能的实现的代码,会不断的在不同的成员函数中用到,但是又不好将这些代码独立出来成为一个类的一

个成员函数。但是又很想复用这些代码。写一个公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复

用这么一片代码,就要单立出一个函数,也不是很好维护。这时就可以用仿函数了,写一个简单类,除了那些维护一个类的成员函数外,就只是实现一个

operator(),在类实例化时,就将要用的,非参数的元素传入类中。这样就免去了对一些公共变量的全局化的维护了。又可以使那些代码独立出来,以便下次

复用。而且这些仿函数,还可以用关联,聚合,依赖的类之间的关系,与用到他们的类组合在一起,这样有利于资源的管理(这点可能是它相对于函数最显著

的优点了)。

二:示例对比

(1)C语言使用函数指针和回调函数来实现仿函数,例如一个用来排序的函数可以这样使用仿函数

#include <stdlib.h>
/* Callback function */
int compare_ints_function(void*A,void*B)
{
	return*((int*)(A))<*((int*)(B));
}
/* Declaration of C sorting function */
void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*));
int main(void)
{
	int items[]={4,3,1,2};
	sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function);
	return 0;
}


(2)在C++里,我们通过在一个类中重载括号运算符的方法使用一个函数对象而不是一个普通函数。

class compare_class
{
	public:
	bool operator()(int A, int B)const{return A < B;}
};
// Declaration of C++ sorting function.
template<class ComparisonFunctor>
void sort_ints(int* begin_items, int num_items, ComparisonFunctor c);
int main()
{
	int items[]={4, 3, 1, 2};
	compare_class functor;
	sort_ints(items, sizeof(items)/sizeof(items[0]), functor);
}


(3)Java中的仿函数是通过实现包含单个函数的接口实现的

List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12");
Comparator<String> numStringComparator =new Comparator<String>()
{
	publicint compare(String o1, String o2){
		returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2));
	}
};
Collections.sort(list, numStringComparator);
三:谓词和仿函数、函数指针

(1)谓词(predicate)一个判断式,一个返回bool值的函数或者仿函数,几元就是函数有几个参数,至于定义和使用,函数定义和一般的函数定义一样;

仿函数就是写个类,然后重载operator()。使用就是在那些以这种需要返回bool值的函数作参数的函数里用了。

(2)所谓的仿函数(functor),是通过重载()运算符模拟函数形为的类。

这里需要明确两点:仿函数不是函数,它是个类;仿函数重载了()运算符,使得它的对你可以像函数那样子调用(代码的形式好像是在调用函数)。但其实问题

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

(3)比较(仿函数和谓词)

仿函数(functor)的优点:我的建议是,如果可以用仿函数实现,那么你应该用仿函数,而不要用回调。原因在于:
仿函数可以不带痕迹地传递上下文参数。而回调技术通常使用一个额外的void*参数传递。这也是多数人认为回调技术丑陋的原因。
话又说回来了,仿函数并不能完全取代回调函数所有的应用场合

(4)比较:

谓词是用以判断参数是否具有某些性质的函数对象(可以是函数,函数指针,仿函数等)(二元谓词有另外的定义

仿函数是重载了operator()的类对象,从而可以如函数一样调用

自定义的比较函数是用于比较两个参数之间是否满足某个关系的函数;谓词和自定义的比较函数都需要返回bool类型或可以转化为bool类型的对象,并且都常常作为一些函数的参数;谓词可以是仿函数

(5)仿函数的优点

1)仿函数比一般函数更灵巧,因为它可以永远状态,事实上对于仿函数,你可以同事拥有两个状态不同的实体,一般函数则力未能逮。
2)每个仿函数都有其类型,因此你可以将仿函数的类型当作template参数来传递,从而指定某种行为模式。此外还有一个好处:容器类型也会因为仿函数的不同而不同
3)执行速度上,仿函数通常比普通函数更快





浅析STL 谓词 + 仿函数 + 函数指针(c)