仿函数和priority_queue

1. STL中的priority_queue模板定义:

template < class T, class Container = vector, 
     class Compare = less > class priority_queue;
//第一个参数是元素类型;
//第二个参数是保存数据的容器,通常是数组类的,比如vector、deque,默认用vector
//第三个参数是元素比较方式,默认用<,缺省时采用内建类型的比较方式。用户可自定义仿函数
priority_queue利用大数堆或者小数堆实现,它是一个基于基本序列容器构建的适配器(queue和stack也是)。priority_queue放置元素时不会去重(因为第二个参数的容器不能保证元素的唯一性)。

priority_queue 调用STL里的 make_heap(), pop_heap(), push_heap() 算法实现,也算是堆的另外一种形式。hit this

2. 仿函数(functor):

仿函数是通过重载()运算符模拟函数行为的类(或者是结构体),也就是仿函数并不是真正意义上的函数,而是通过类对象的调用来模拟函数的行为。之前在自定义set的比较函数中实现了一个简单的仿函数,当时还不太理解,它是STL六大组件(容器,迭代器,算法,仿函数,配置器?,配接器?)之一,仿函数拓展了算法的功能,几乎所有的算法都支持仿函数版本。

仿函数,又叫函数对象(《数据结构与算法分析 C++描述》),函数对象调用时不是使用函数名来调用函数,而是使用操作符重载。函数对象提供两大优势的函数调用。第一个是函数对象可以包含状态,第二是函数对象属于类型并可用作模板参数。具体方法是,定义一个包含零个数据和一个成员函数的类(视情况而定,下面的例子是这样的,数据成员通常用来表征状态,这也正是仿函数的魅力所在),然后传递这个类的实例,从效果上看,通过将其在对象中实现了的函数的传递。该对象通常称为函数对象(function object)。例如:

template 
const Object & findMax(const vector & arr, Comparator cmp)
{
	int maxIndex = 0;
	for (int i = 1; i < arr.size(); i++)
		if (cmp.isLessThan(arr[maxIndex], arr[i]))
			maxIndex = 1;

	//other codes
	// ... ...
}
class CaseInsensitiveCompare
{
public:
	bool isLessThan(const string & lhs, const string & rhs) const
	{	return stricmp(lhs.c_str(), rhs.c_str()) < 0;}
};

int main()
{
	//call
	findMax(arr,CaseInsensitiveCompare());

	//other codes
	// ... ...

	return 0;
}c++的函数对象有一些奇特的语法,函数对象调用时不是使用函数名来调用函数,而是使用操作符()的重载。即不是使用函数isLessThan,而是使用operator(),当调用operator()时,cmp.operator()(x,y)可以缩写为cmp(x,y),换句话说,这看起来就像是函数调用,因此,operator()被称为函数调用操作符(function call operator),其参数改成更容易理解的isLessThan。 
  

更为晦涩一点的版本是直接重载operator()函数,并把比较在重载的函数中做,返回bool值。也就是自定义set的比较函数中的形式。

3. Next,《STL,标准模板库》p123以及第8章对仿函数的特点的用法进行了详细的介绍:(下面自己总结的很烂,感兴趣的话还是直接看书吧)

 仿函数的优势:1)smart functions。仿函数可拥有成员函数和成员变量,也就是仿函数可以拥有状态信息,通过不同的状态来表征不同的行为,更加灵活。在同一时间里由某个仿函数所代表的单一函数,可能有不同的状态,这在一般函数中是不可能的;2)每个仿函数都有自己的型别,这对于“利用template实现泛型编程”是一个卓越的贡献。也就是说从本质来看,每一个仿函数都属于一种类型,这可以在定义模板时将其作为模板参数,而这是普通函数做不到的。3)仿函数通常比一般函数快一些。由于template通常在编译期间就已确定,所以传入一个仿函数可能会获得更好的性能。

带有状态的仿函数的使用方法:

// 1. 构造一个带有数据成员theValue的AddValue类(用作函数对象)
// 2. 举个例子
for_each(a.begin(), a.end(), AddValue(10));
// 说明,其中的AddValue(10)生成一个AddValue对象,并以10作为初值,AddValue构造函数将这个值保存在成员theValue
多状态的优势:

// 下面看“一个函数,两个状态”
AddValue addx(x);
AddValue addy(y);
// ... ... 
for_each(a.begin(), a.end(), addx);
for_each(a.begin(), a.end(), addy);
// 上述两个表达式分别调用了不同状态的仿函数,very cool!
仿函数通常是通过by value方式调用,每次调用不会改变其内部状态。但是也可以使用by reference方式调用,这样的话可以改变其内部状态

IntSequence seq(1); //声明一个仿函数,将内部数据成员初始化为1.
generate<... , int, IntSequence&> (a, int, seq); //调用函数generate时,将其template参数明白加以标示(将IntSequence重写为IntSequence&),下次再使用seq时,其内部数据成员的值已经改变(该generate函数结束时的值)

另外,for_each()可以返回其使用的仿函数的最终状态:

AddValue av = for_each(a.begin(), a.end(), addx);
此时如果AddValue中定义了一些打印信息的函数,可以直接调用av.print_func()打印出信息确认。


参考:msdn 函数对象, and使用lambda函数,函数对象和受限函数




你可能感兴趣的:(C/C++,Notes,for,Bks)