函数对象
网上对函数对象这个概念的说法有很多啊,特别是百度百科里的函数对象和仿函数,说的玄乎死了
按照我自己的理解吧,函数对象是一个既可以像正常函数使用,又可以作为参数,参与另一个函数的一种东西
函数对象又称为仿函数,是STL的组件之一。通过图片可以看出,函数对象主要服务于算法。
函数对象有四种实现方法
- 函数指针
lambda
表达式- 带有成员函数
operator()
的class建立的object - 带有转换函数可以将自己转换为pointer to function的class所建立的object
函数指针
其实在前面一些小笔记(1)的开始就记载过
bool cmp(int x,int y){
return x>y;
}
sort(arr,arr+len,cmp);
sort()
的第三个参数,不就是函数指针吗?
再简单点来说,函数名就是函数的入口,就是函数指针
想想平时的编程,定义了一个函数,需要用到时,直接写上函数名,带上参数,就调用成功了
函数名是不是函数指针,各有各的说法,有点说是,有的说不是
上面的例子看起来是函数指针吧
但后面学Qt
时,又似乎函数名不是函数指针
void (Teacher::*noParaT)() = &Teacher::hungry;
void (Student::*noParaS)() = &Student::treat;
connect(zt,noParaT,st,noParaS);
第一二行就是在定义函数指针,其实用这个例子来说明函数名不是函数函数指针还有点牵强
这里connect()
第二第四个参数需要指针,但信号和槽这两个函数,都是可以重载的,或许是为了避免编译时,省去检测是否有重载函数等操作,而特别规定不能使用函数名作为参数呢?
但又为什么要对函数名进行取址呢 ??
当去掉等号后面的&
,报错了
error: call to non-static member function without an object argument
行吧,函数名就是函数指针
上面Qt
的例子,原因说不准就是我说的那样🤔
#include <iostream>
using namespace std;
int fun1(int tmp1, int tmp2)
{
return tmp1 * tmp2;
}
int fun2(int tmp1, int tmp2)
{
return tmp1 / tmp2;
}
int main()
{
int (*fun)(int x, int y);
fun = fun1;
cout << fun(15, 5) << endl;
fun = fun2;
cout << fun(15, 5) << endl;
return 0;
}
函数指针的定义方法
其实和定义普通指针差不多,只需要在指针名后面加上函数的参数列表,再把*
和指针名
用()
括起来
int n = 10;
int *p = &n;
int fun(int a,int b){
return a+b;
}
int (*p) (int a,int b); // 函数指针
p = fun;
顺便提一下指针函数吧,返回指针的函数
#include <iostream>
using namespace std;
struct Type
{
int var1;
int var2;
};
Type * fun(int tmp1, int tmp2){
Type * t = new Type();
t->var1 = tmp1;
t->var2 = tmp2;
return t;
}
int main()
{
Type *p = fun(5, 6);
return 0;
}
lambda
表达式
还是前面记载过了,一些小笔记(1),只是之前只知道lambda
表达式是一个匿名函数,也只知道它可以用在sort()
函数上,现在也算补充了对它的认识,用来实现函数对象或者说仿函数
int main(){
vector<string> v = { "I", "love", "CPlusPlus" };
sort(v.begin(), v.end(),
[](string l, string r)
{ return l.size() > r.size(); });
for(auto s : v)
cout << s << " ";
}
程序输出
CPlusPlus love I
带有成员函数operator()
的类的对象
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
百度百科对仿函数的定义在这里倒挺合适的,但这个是函数对象的实现方法之一啊,而仿函数就是函数对象,简直乱套了
class CompareSize
{
public:
bool operator() (const string& lhs, const string& rhs)
{
return lhs.size() > rhs.size();
}
};
int main(){
vector<string> v = { "I", "love", "CPlusPlus" };
sort(v.begin(),v.end(),CompareSize()); // 第一种写法
CompareSize cmp;
sort(v.begin(), v.end(),cmp); // 第二种写法
for(auto s : v)
cout << s << " ";
return 0;
}
两种写法,类中不管是否还有其他成员,好像都不影响
再看上面百度百科对仿函数的定义,使一个类的使用看上去像一个函数,那类就相当于函数,类名就当函数名来使用;而对象又是类的实例化,也当函数名来使用
这里还可以定义结构体,来实现sort()
的条件谓词
struct CompareSize
{
bool operator() (const string& lhs, const string& rhs)
{
return lhs.size() > rhs.size();
}
};
sort(v.begin(),v.end(),CompareSize()); // 实现方法
带有转换函数的类的对象
这个不是很明…
谓词
谓词(predicate)可以是一个返回值,是
bool
类型的普通函数或者重载了operator()
的函数对象(仿函数)。标准库算法中用到的谓词要么是一元谓词(Unary Predicate),接受一个参数;要么是二元谓词(Binary Predicate),接受两个参数
构造谓词有两种方法,普通函数和函数对象
普通函数
一元谓词greater10
:
bool greater10(int val){
return val>10 ;
}
函数greater10接受一个int类型的参数,若参数大于10,返回true,否则返回false
二元谓词twice:
bool twice(int val1,int val 2)
{
return (val1 == 2*val2);
}
若val1是val2的两倍,则true
函数对象
这里所说的函数对象,其实就是上面函数对象实现的四个方法中的第三个
下面是书本对此处的函数对象的定义
函数对象是一个重载了函数运算符
operator()
的类,该类的对象可以像函数一样进行调用
倒挺形象的,这里就不赘述了
总结
写这篇博客,一开始是为了搞清楚谓词,而书本提到构造谓词的第二种方法 —— 函数对象,所以又跑去了解函数对象,
但搞完后,我好像觉得广义的函数对象其实就是谓词?
这里说的广义,就是指上面有四种实现方法的函数对象,而不是谓词的实现方法之一的函数对象
上面的函数对象,有四种实现方法,而前面三种都可以拿来作为sort()
的第三个参数,而且函数指针不就是谓词里说到的第一个方法吗?
补充
这里对函数对象的第三种实现方法 —— 带有成员函数operator()
的类的对象,补充一点操作说明
#include <iostream>
using namespace std;
class Square{
public:
double operator()(double x) const{
return x*x;
}
int operator()(int a,int b,int c=10) const{
return a+b-c;
}
void operator()()const{
cout<<"!!!";
}
};
int main(){
Square s;
cout<<s(10.0)<<endl; // 100.0
cout<<s(10,10,5)<<endl; // 15
cout<<s(5,5)<<endl; // 0
cout<<s()<<endl; // 报错,void类型没有返回,如何"cout<<" ?
s(); // !!!
s; // 不会报错 也没有输出
Square(); // 不会报错 也没有输出
Square; // 不会报错 也没有输出
}
这里说白了就是重载了()
,然后类的对象调用了这个操作符
所以看到这,发现百度百科对仿函数的简单描述还真就是这么回事。。。只是一开始自己没转过弯来
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
这里再看函数对象的百度百科,好像也就那么回事,而且这里面还有介绍谓词