三、函数对象(类似C的函数指针)
函数对象 =》 C语言里面的函数指针
使用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) //Compare推导出来是函数指针
{
//通过函数指针调用函数,是没有办法内联的,所以会有函数调用开销,效率很低
return comp(a, b);
}
int main()
{
cout << compare(1, 2, mygreater<int>) << endl; //0
cout << compare(1, 2, myless<int>) << endl; //1
}
通过函数指针调用函数,为什么没有办法内联?
答:编译阶段comp函数指针并不知道它具体要调用mygreater还是myless,所以没有办法内联,只有运行的时候才会跑到这个函数指针具体指向的地址上执行指令
通过函数指针调用函数,是没有办法内联的,所以会有函数调用开销,效率很低。
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;
}
};
template<typename T, typename Compare>
bool compare(T a, T b, Compare comp) //Compare推导出来是函数对象
{
return comp(a, b); //operator()(a,b);
}
int main()
{
//现在传入的是mygreater与myless的无名对象,Compare推导出来的就是具体对象的operator()(a,b),即编译过程中comp知道具体调用的是哪个对象的哪个函数,所以可以内联,省下函数调用开销,调高效率
cout << compare(1, 2, mygreater<int>()) << endl; //0
cout << compare(1, 2, myless<int>()) << endl; //1
}
使用函数对象的好处:
- 通过函数对象调用operator(),可以通过内联省略函数的调用开销,比通过函数指针调用函数(不能内联)效率高。(使用函数对象虽然我们没有显示加上inline关键字,但编译器优化时可能会优化成内联)
- 因为函数对象是用类生成的,所以类中可以添加相关的成员变量,用来记录函数对象使用时的更多信息
函数对象使用示例1:将优先级队列从大根堆修改为小根堆
priority_queque原型:第三个参数默认是less,修改为greater就可以实现小根堆了
#include <queue>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//输出优先级队列,默认从大到小,底层vector,大根堆
priority_queue<int> que1;
for (int i = 0; i < 10; ++i)
{
que1.push(rand() % 100);
}
cout << "默认输出顺序:(大根堆)" << endl;
while (!que1.empty())
{
cout << que1.top() << " ";
que1.pop();
}
cout << endl << endl;
//通过修改优先级队列的函数对象,使得其组织方式从大根堆变成小根堆,从小到大输出
using MinHeap = priority_queue<int, vector<int>, greater<int>>;//默认第三个参数是less,修改为greater就变成小根堆了
MinHeap que2; //底层vector,大根堆
for (int i = 0; i < 10; ++i)
{
que2.push(rand() % 100);
}
cout << "修改为小根堆之后的输出结果:" << endl;
while (!que2.empty())
{
cout << que2.top() << " ";
que2.pop();
}
cout << endl << endl;
}
函数对象使用示例2:将set从小到大输出改变为从大到小输出,set底层是红黑树
set原型:第二个参数是函数对象,默认是less,修改为greater就可以从大到小输出了
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> set1;
for (int i = 0; i < 10; ++i)
{
set1.insert(rand() % 100);
}
cout << "set默认输出:" << endl;
for (int v : set1)
{
cout << v << " ";
}
cout << endl << endl;
set<int, greater<int>> set2;
for (int i = 0; i < 10; ++i)
{
set2.insert(rand() % 100);
}
cout << "set将less改成greater后的输出:" << endl;
for (int v : set2)
{
cout << v<<" ";
}
cout << endl << endl;
}