仿函数functors与函数对象function objects

27 篇文章 2 订阅

定义

仿函数是早期的名称,c++标准规格定案后所采用的新名称是函数对象。

仿函数是一个行为类似函数的对象。 为了能够 “行为类似函数”,其类别定义中必须自定义function call运算子(operator() )。拥有这样的运算子后,我们就可以在仿函数对象后面加上一对小括号,以此调用仿函数所定义的operator()。

仿函数(functor)又称为函数对象(function object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载operator()运算符。

class A {
public:
    int operator() (int a, int b) {
        return a + b;
    }
};

int main() {

    A objA;
    int c = objA(1,2);

}

 

仿函数的作用与意义:

1) 使用

先考虑一个简单的例子:假设有一个vector<string>,你的任务是统计长度小于5的string的个数,如果使用count_if函数的话,你的代码可能长成这样:

bool LengthIsLessThanFive(const string& str) {
      return str.length()<5;    
 }
int res=count_if(vec.begin(), vec.end(), LengthIsLessThanFive);//stl自动把每个被遍历的元素传递给函数

其中count_if函数的第三个参数是一个函数指针,返回一个bool类型的值。一般的,如果需要将特定的阈值长度也传入的话,我们可能将函数写成这样:

bool LenthIsLessThan(const string& str, int len) {
    return str.length()<len;
}

这个函数看起来比前面一个版本更具有一般性,但是他不能满足std::count_if函数的参数要求:count_if要求的是unary function(仅带有一个参数)作为它的最后一个参数。所以问题来了,怎么样找到以上两个函数的一个折中的解决方案呢?

这个问题其实可以归结于一个data flow的问题,要设计这样一个函数,使其能够access这个特定的length值,回顾我们已有的知识,有三种解决方案可以考虑:

1、函数的局部变量;

      局部变量不能在函数调用中传递,而且caller无法访问。

2、函数的参数;

  这种方法我们已经讨论过了,多个参数不适用于count_if函数。

3、全局变量;

  我们可以将长度阈值设置成一个全局变量. 但是太丑陋。

 

有什么解决方案呢? :仿函数。

我们的初衷是想设计一个unary function,使其能做binary function的工作,这看起来并不容易,但是仿函数能解决这个问题。

仿函数其实是上述解决方案中的第四种方案:成员变量。成员函数可以很自然的访问成员变量,可以将有用信息存储到成员变量中,相当于用成员变量代替入参。

从而可以定义如下函数对象:将比较的长度基准作为一个成员变量。

class ShorterThan {
    public:
        explicit ShorterThan(int maxLength) : length(maxLength) {}
        bool operator() (const string& str) const {
            return str.length() < length;
        }
    private:
        const int length;
};
length = 5;
count_if(myVector.begin(), myVector.end(), ShorterThan(length));

 

my Test code:

#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;
class functor
{
public:
	functor(int len=0):length(len)
	{
		std::cout << "functor::functor() was called!" << endl;
	}

	~functor() {
		std::cout << "functor::~functor() was called!" << endl;
	}

	bool operator()(string val) {
		if (val.length() >= length)
			return true;
		else
			return false;
	}
private:
	int length;
};

//判断string的长度是否大于等于4
bool countString(string strVal) {
	if (strVal.length() >= 4)
		return true;
	else
		return false;
}
int main()
{
	std::vector<string> stringVec = {"ab","abc" ,"abcd","m" ,"dddddddd" };
	int count;
	count = std::count_if(stringVec.begin(),stringVec.end(), countString);
	cout << "count with pointer =" << count << std::endl;

	count = std::count_if(stringVec.begin(), stringVec.end(), functor(4));
	cout << "count with functor =" << count << std::endl;

	
	return 0;
}

输出:(为何调用了两次析构函数丫丫)

 

https://www.cnblogs.com/decade-dnbc66/p/5347088.html

https://www.cnblogs.com/shaonianpi/p/10386963.html

https://blog.csdn.net/zhangyueweia/article/details/50440639

 

2) STL

c++ stl提供的各种算法,往往有两个版本,其中一个版本表现出最常用的某种运算,第二个版本表现出最泛化的演算流程,允许用户以template参数来指定所要采取的策略。  拿accumulate()来说,其一般行为(第一版本)是将指定范围内的所有元素相加,第二版本则允许你指定某种操作,取代第一版本中的相加行为。 以sort()为例,其第一版本是以operator< 为排序时的元素位置调整依据,第二个版本则允许用户指定任何操作,务求排序后相邻两元素都能令该操作的结果为true.

要将某种操作当作函数的入参,一个方法是将该种操作设计为一个函数,然后,将函数指针作为函数的入参,另一个方法是将该种操作设计为一个仿函数(就语言层面而言是个class), 再以该仿函数产生一个对象,并以此对象作为算法的一个参数。

嗯,既然函数指针可以满足要求,那么又何必又所谓的仿函数呢? 原因在于函数指针不能满足stl对抽象性的要求,也不能满足软件积木的要求--函数指针无法和stl其他组件(如配接器adapter)搭配,产生更灵活的变化。

 

3)性能优缺点


https://blog.csdn.net/yasi_xi/article/details/8301545
https://www.dazhuanlan.com/2019/10/15/5da56ee8db9c3/

 


Ref:

<STL源码剖析> chapter 7.1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

First Snowflakes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值