持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情
- 什么是仿函数
- 仿函数的作用
仿函数
仿函数又称为函数对象,可能你对函数和对象这两个概念都比较熟悉,但是函数对象在一起可能会感觉有些陌生,下面通过一个例子给大家解释一下什么是函数对象,以及函数对象和函数区别,以及我们为什么要引入函数对象。
仿函数应用
偏函数实现
struct Object
{
int operator()(int i) const{
return i;
}
};
int func(int i)
{
return i;
}
int main()
{
Object obj;
int a = 2;
int res = obj(a);
int res_func = func(a);
cout << "result of functor: " << res << endl;
cout << "result of function: " << res << endl;
cout<<"Hello World";
return 0;
}
这里我们来看函数对象和对象函数使用并没有什么区别,接下来我们来看一个一个有趣的例子
struct AddFunctor
{
int operator()(int i,int j) const{
return i;
}
};
int Add(int i,int j)
{
return i;
}
int main()
{
AddFunctor addFunctor;
int a = 1;
int b = 2;
addFunctor(a,3);
addFunctor(b,3);
Add(a,3);
Add(b,3);
cout<<"Hello World";
return 0;
}
上面例子我们不难看出进行两次加法运算,都使用一个共同数值 3,那么我们来看一看如何通过函数对象对其进行优化。因为函数对象本质是一个对象所以其内部可以持有状态,现在让我们对上 AddFunctor 做适当调整,我们可以把其中一个参数作为状态来维护
struct AddFunctor
{
AddFunctor(int t):m_t(t){};
int operator()(int i) const{
return i + m_t;
}
int m_t
};
在 AddFunctor 创建一个变量 m_t 持有状态, 也就是将加法分解为一个因子固定加法,这些在 JavaScript 中使用通过 curry 化来实现偏函数。其实这就是我们熟悉偏函数,也是函数式编程中常见一种函数式编程思想的体现。
AddFunctor addFunctor(3);
int a = 1;
int b = 2;
addFunctor(a);
addFunctor(b);
Add(a,3);
Add(b,3);
这里例子看做不算明显,我们举一个相对复杂的例子来进一步说明
void doSomething(int i, bool b, float f,double d,unsigned int u, char c){
}
doSomething(1,true,2.f,3.0,5u,'a');
doSomething(1,true,2.f,3.0,5u,'b');
观察一下两次调用doSomething 函数 doSomething(1,true,2.f,3.0,5u,'a'); 就不难发现其中大部分参数都是重复的,我们可以将重复作为函数对象的状态所持有,这样简化工作量,代码也易于维护。
struct DoSomethingFunctor
{
DoSomethingFunctor(int i,bool b, float f, double d,unsigned int u):
m_i(i),m_b(b),m_f(f),m_d(d),m_u(u){}
void operator()(char c)const {
}
int m_i;
bool m_b;
float m_f;
double m_d;
unsigned int m_u;
};
DoSomethingFunctor o(1,true,2.f,3.0,5u);
o('a');
o('b');
排序
接下来通过一个自定义排序方式来看一看仿函数的另一个应用
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
struct Tut
{
Tut(int idx,string title):m_idx(idx),m_title(title){}
unsigned int m_idx;
string m_title;
friend ostream& operator<<(ostream& os,const Tut& tut)
{
os << tut.m_title ;
return os;
}
};
int main()
{
vector<Tut> tuts{Tut(1,"machine learning"),Tut(2,"probability grahical model"),Tut(5,"deep learning"),Tut(3,"segmenation")};
cout << Tut(1,"machine leanring") << endl;
for(auto it = tuts.begin(); it != tuts.end(); it++){
cout << *it << endl;
// cout << " " << endl;
}
cout<<"Hello World";
return 0;
}
#include<iostream>
#include<string>
#include<vector>
#include <bits/stdc++.h>
class Message
{
public:
std::string getHeader(const std::string& header_name) const;
};
sort(tuts.begin(),tuts.end());
我们尝试对 tuts 的元素进行排序,因为 Tut 并没有覆写 < 方法所以需要我们提供覆写这个 operator<
error: no match for ‘operator<’ (operand types are ‘Tut’ and ‘Tut’)
bool operator< (Tut const& other) const{
return m_idx < other.m_idx;
}
我们根据错误提示覆写 operator< 方法, 如果我们想要课程引入价格,然后按价格进行排序。
struct Tut
{
Tut(int idx,string title, float price):m_idx(idx),m_title(title),m_price(price){}
unsigned int m_idx;
float m_price;
string m_title;
bool operator< (Tut const& other) const{
return m_idx < other.m_idx;
}
friend ostream& operator<<(ostream& os,const Tut& tut)
{
os << tut.m_title ;
return os;
}
};
我们可以去修改bool operator< (Tut const& other) const函数内部逻辑,这样可能会影响其他排序位置的逻辑
struct SortByPriceFunctor
{
bool operator()(Tut const& lhs, Tut const& rhs)const{
return lhs.m_price < rhs.m_price;
}
};
int main()
{
vector<Tut> tuts{
Tut(1,"machine learning",10.f),
Tut(2,"probability grahical model",15.f),
Tut(5,"deep learning",20.f),
Tut(3,"segmenation",5.f)};
sort(tuts.begin(),tuts.end(),SortByPriceFunctor());
// cout << Tut(1,"machine leanring") << endl;
for(auto it = tuts.begin(); it != tuts.end(); it++){
cout << *it << endl;
// cout << " " << endl;
}
// cout<<"Hello World";
return 0;
}
class MessageSorter
{
public:
MessageSorter(const std::string& field) : _field(field) {}
bool operator()(const Message& lhs, const Message& rhs)
{
return lhs.getHeader(_field) < rhs.getHeader(_field);
}
private:
std::string _field;
};
int main()
{
std::vector<Message> messages;
MessageSorter comparator("some string");
sort(messages.begin(), messages.end(), comparator);
std::cout << "hello Functor" << std::endl;
}
我们可以通过仿函数来实现在列表中搜索,
仿函数和函数指针
如果接受函数指针的函数,不能像传递函数指针一样传递仿函数,即使仿函数的参数和返回值与函数指针的参数相同。通常需要传入仿函数,则不能传入一个函数指针
Functors vs. Virtual Functions
其实仿函数和虚函数是密切相关的,都可以解决了同一类问题,例如我们创建函数提供了。这里设计一个叫 doMath 的函数,这个函数需要 3 个参数:两个整型的数值以及要这两个数值进行操作的运算操作,这个需求可以仿函数来实现。
typedef int FuncType(int x, int y);
这里定义类型 FuncType 一个函数类型 int FuncType(int x, int y)
class MathComputer
{
public:
virtual int computeResult(int x, int y) = 0;
};
int doMath(int x, int y, MathComputer* p_computer)
{
return p_computer->computeResult(x, y);
}
class Add :MathComputer {
public:
int computeResult(int x, int y) {
return x + y;
}
};
首先定义类似抽象类定义 MathComputer 类中定义虚函数 virtual int computeResult(int x, int y) 通过继承于 MathComputer 类并实现需函数来实现其中虚函数来实现多态。
int main()
{
MathComputer* mathComputer ;
Add add;
mathComputer = (MathComputer*)& add;
int res = doMath(2,3, mathComputer);
std::cout << res << std::endl;
return 0;
}