一、函数对象的概念
说到函数对象这个概念,可能大家还是比较陌生的,那首先,我们从我们熟悉的c语言里面的函数指针说起。
我们同样实现sum函数,一个是在c语言里面用函数指针实现,一个是使用c++里面的对象实现,如下图所示:
来看看有什么不同,左边的C语言实现方式传入的是函数的地址,而后面的c++方式则传入的是sum对象,其对象通过调用operator函数来实现功能。
由此,就可以给出函数对象的具体概念了。
把有operator()小括号运算符重载函数的对象,称作函数对象或者称作仿函数
二、函数对象的好处
(1)实现compare版本一:
template<typename T>
bool compare(T a, T b)
{
return a > b;
}
int main()
{
cout << compare(10, 20) << endl;
cout << compare('b', 'y') << endl;
return 0;
}
仔细观察上述函数的实现,我们可以思考一下,如果想实现小于怎么办?我么不能把库里面的函数进行频繁更改,所以引出了c语言函数指针的解决方案
(2)使用c的函数指针解决方案
template<typename T>
bool mygreater(T a, T b)
{
return a > b;
}
template<typename T>
bool myless(T a, T b)
{
return a < b;
}
template<typename T,typename Compare>
bool compare(T a, T b, Compare comp)
{
return comp(a, b);
}
int main()
{
cout << compare(10, 20, mygreater<int>) << endl;
cout << compare(10, 20, myless<int>) << endl;
return 0;
}
运行结果如下:
仔细观察上述函数的实现,通过函数指针调用函数,是没有办法内联的,效率很低,因为有函数调用开销。
尽管把要调用的函数写成内联函数也不可,因为函数指针是在运行的时候去调用,但是内联函数要在编译的时候就展开
由此,我们就衍生出了函数对象的解决方案
(3)c++函数对象的版本实现
template<typename T>
class mygreater
{
public:
bool operator()(T a, T b)
{
return a > b;
}
};
template<typename T>
class myless
{
public:
bool operator()(T a, T b)
{
return a < b;
}
};
int main()
{
//此时传入的是两个对象而不是函数的地址
cout << compare(10, 20, mygreater<int>()) << endl;
cout << compare(10, 20, myless<int>()) << endl;
return 0;
}
函数对象的好处:
1、通过函数对象调用operator(),可以省略函数的调用开销
编译过程中调用的是哪个对象的哪个函数是非常明确的,因此使用函数对象是完全可以内联的,省去了函数调用开销
2、因为函数对象是用类生成的所以可以添加相关的成员变量用来记录函数对象使用时更多的信息。
比如,想记录这个对象被调用了多少次,就可以在私有成员里面定义一个count成员变量计数
三、函数对象的应用
应用一:优先级队列
在优先级队列中,默认的是大根堆,但是我们可以改变底层比较的方式来实现小根堆,代码如下所示:
int main()
{
priority_queue<int> que1;//默认的是大根堆
for (int i = 0; i < 10; i++)
{
que1.push(rand() % 100);
}
while (!que1.empty())
{
cout << que1.top() << " ";
que1.pop();
}
cout << endl;
//小根堆 改变底层比较的方式
using MinHeap = priority_queue<int, vector<int>, greater<int>>;
MinHeap que2;
for (int i = 0; i < 10; i++)
{
que2.push(rand() % 100);
}
while (!que2.empty())
{
cout << que2.top() << " ";
que2.pop();
}
cout << endl;
return 0;
}
应用二:set
在set中系统默认的是less,我们来实现从大到小的排列
int main()
{
set<int,greater<int>> set1;
for (int i = 0; i < 10; i++)
{
set1.insert(rand() % 100);
}
for (int v : set1)
{
cout << v << " ";
}
cout << endl;
return 0;
}