函数对象和谓词


函数对象

网上对函数对象这个概念的说法有很多啊,特别是百度百科里的函数对象和仿函数,说的玄乎死了
按照我自己的理解吧,函数对象是一个既可以像正常函数使用,又可以作为参数,参与另一个函数的一种东西



函数对象又称为仿函数,是STL的组件之一。通过图片可以看出,函数对象主要服务于算法。





函数对象有四种实现方法

  1. 函数指针
  2. lambda表达式
  3. 带有成员函数operator()的class建立的object
  4. 带有转换函数可以将自己转换为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(),这个类就有了类似函数的行为,就是一个仿函数类了。


这里再看函数对象的百度百科,好像也就那么回事,而且这里面还有介绍谓词

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值