C++中的仿函数functor

2023-10-27

引子

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

bool LessThan5Function(const string& str) {//寻找长度小于5的字符串
	return str.length() < 5;
}

void test() {
	vector<string> sVec{ "sadaw","srfafas","12" };
	int count = count_if(sVec.begin(), sVec.end(), LessThan5Function);//寻找容器中长度小于5的字符串的数量
}

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

bool LessThan5Function(const string& str,size_t len) {//寻找长度小于5的字符串
	return str.length() < len;
}

这个函数看起来比前面一个版本看起来OK,因为更加具有可操作性,但是他不能满足这个泛型函数count_if的参数要求:count_if要求的函数是unary function(仅带有一个参数的函数)。

我们如何才能在只能传入一个参数的情况下使用这个count_if,并且指定自己想要的参数,而且,不需要改变这个函数本身,比如如果我们要小于7的字符串数量,就得将传入的函数写成这样的话,正常人都会崩溃的

bool LessThan7Function(const string& str) {
	return str.length() < 7;
}

一般这种情况怎么办?

我们应该会想到用一个全局变量

int maxLength = 5;
bool LessThan5Function(const string& str) {//寻找长度小于5的字符串
	return str.length() < maxLength;
}

但是全局变量的缺点相信大家都很清楚:

  • 污染命名空间(多人协同,冲突)
  • 每次修改功能还是需要去修改这个全局变量
  • 不具有重复使用的功能(比如多个要用到这个函数指针的泛函数)

所以,说了这么多,我们需要的是一个什么东西呢?

我们需要一个能够实现函数功能,并且可以携带我们需要的数据(比如上面的字符串长度),最后最重要的:不会因为多个人同时使用,而造成功能冲突和混乱,的这样的一个东西

这就是我将要介绍的仿函数:

仿函数

借用一下百度百科的解释:

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是在类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

将上面的那个函数,用仿函数的方式改写了之后是什么样子?

class LessThan5Functor {
public:
	bool operator ()(const string& str) {//寻找长度小于5的字符串
		return str.length() < 5;
	}
};

就是一个简单的operator ()操作符的重写,但是仿函数和上面的函数指针不同的一点就是:它是一个对象

什么意思呢?

就是这是一个可以被实例化的、具有内存的、可存储数据的实体

想一下,上面的函数指针,我们想要携带数据进入那个count_if是多么的麻烦和不具有普适性,而仿函数的出现,就是为了兼容STL的标准

仿函数的使用

int main()
{
	greater<int > gt;
	cout << gt(1, 2) << endl;//false;   (1)
	cout << greater<int >()(6, 4) << endl;//true;    (2)

	system("pause");
	return 0;
}

上面的代码,就是一个仿函数的使用例子,我们定义了一个greater<int>的对象gt,(1) 中gt(1, 2)就是直接调用了greater<int> gt的的operator ()函数

而(2)是定义了一个greater<int>的局部变量,然后调用其operator ()函数,在(2)的这行代码结束后,这个greater<int>的局部变量就被回收了(临时对象)

善用仿函数的成员变量

针对最开始我们说的多个参数问题,我们可以如何解决呢?

int count = count_if(sVec.begin(), sVec.end(), LessThan5Functor);//寻找容器中长度小于5的字符串的数量

我们可以传入一个LessThanLenFunctor的仿函数对象作为参数,这个对象内部保存了长度信息,LessThanLenFunctor的对象实现如下:

class LessThanLenFunctor {
public:
	bool operator ()(const string& str) {//寻找长度小于5的字符串
		return str.length() < len;
	}
	LessThanLenFunctor(int l):len(l) {}
private:
	int len ;
};

这样我们就避免了使用全局变量的那几个问题,而且,非常优雅且有用,我们如何去使用这个仿函数呢?

如下,假设我们想查找长度小于3的字符串:

void test() {
	vector<string> sVec{ "sadaw","srfafas","12" };
	int count = count_if(sVec.begin(), sVec.end(), LessThanLenFunctor(3));//寻找容器中长度小于3的字符串的数量
}

我们只需要传入一个内部成员变量len为3的仿函数对象就行了,无论长度如何变化,有多少人要使用多少个不同的仿函数,你的仿函数的功能都不会发生变化

它既能像普通函数一样传入给定数量的参数(一个或者多个),还能存储我们需要的控制信息

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++中的仿函数functor 的相关文章

  • Postsharp 不登录跟踪级别

    我喜欢在跟踪级别记录一些 Postsharp 消息 不幸的是 日志到这个级别没有打印任何输出 所有其他级别都在工作 与控制台或 NLog 后端或从其他类登录时的行为相同 如何启用跟踪级别 应用程序 xaml cs Log Attribute
  • 为什么我应该使用内联代码? [复制]

    这个问题在这里已经有答案了 我是一名 C C 开发人员 这里有几个始终困扰我的问题 常规 代码和内联代码之间有很大区别吗 主要区别是什么 内联代码只是宏的一种 形式 吗 选择内联代码时必须进行什么样的权衡 Thanks 表现 正如之前的答案
  • OpenCV SVM 给出奇怪的预测结果

    我对 OpenCV 和支持向量机都很陌生 我想使用 SVM 训练具有两个标签的数据集 然后预测给定集合的标签 我当前的集合包含大约 600 行 具有相等的类分布 1 为 300 行 1 为 300 行 包含 34 列 这是我当前用于设置 O
  • 更改图像颜色与透明背景

    我需要使用 c System Drawings 将透明背景上带有绿色圆圈的图像加载到位图图像中 这是最简单的部分 但是 我需要在将其添加到更大的图像之前更改圆圈的颜色 而不影响周围的透明度 就我而言 我需要将圆圈颜色更改为黄色并将其添加为太
  • C 中的 '\0' 和 printf()

    在 C 入门课程中 我了解到在存储字符串时存储空字符 0在它的最后 但是如果我想打印一个字符串怎么办 printf hello 虽然我发现它并没有结束 0通过以下声明 printf d printf hello Output 5 但这似乎不
  • C 链表销毁函数

    我正在尝试学习 C 和很多人一样 我对指针有点困惑 无论如何 我创建了一个递归函数来销毁我的链表 但是正如我调试的那样 当我从函数返回时 列表的头部不应该为空 所以我猜这是对指针的一些基本误解 这是函数 void destroy struc
  • 有没有办法找到dll公开的所有函数

    我一直在寻找一种方法来获取映射到 dll 中函数名称的所有字符串 我的意思是您可以调用 GetProcAddress 的所有字符串 如果你对 dll 进行十六进制转储 符号 字符串 就在那里 但我认为必须有一个系统调用来获取这些名称 如果您
  • 如何在Unity Inspector中创建多维数组?

    如何在 Unity Inspector 中创建枚举多维数组并使其可序列化 以便我可以从不同的脚本调用它 public enum colors red blue green yellow cyan white purple public in
  • FFplay成功移入我的Winform中,如何设置它无边框?

    用这个代码 在 C 应用程序中显示 tcp 视频流 来自 FFPLAY FFMPEG https stackoverflow com questions 14201894 show a tcp video stream from ffpla
  • 如何在 C 语言中获取输入中的空格

    我想从控制台获取字符数组 它还包含空格 我在 C 中知道的唯一方法是 scanf 但是一旦遇到空格 它就会停止接受输入 我该做什么 这就是我正在做的事情 char address 100 scanf s address 尝试使用 fgets
  • 解析连接字符串

    是否有标准库或代码片段可以使用这样的连接字符串获取值 string connstr DataServiceUrl http localhost foo RemoteServerConnection server http localhost
  • 为什么最小的 int -2147483648 的类型为“long”? [复制]

    这个问题在这里已经有答案了 对于一个学校项目 我必须编写 C 函数 printf 的代码 一切进展顺利 但有一个问题我找不到好的答案 所以我来了 printf PRINTF d t d n 2147483648 告诉我 gcc Werror
  • 如何将 Boost Spirit 自动规则与 AST 结合使用?

    编辑 当我想在另一个规则上使用它时 我扩展了 sehe 的示例以显示问题 http liveworkspace org code 22lxL7 http liveworkspace org code 22lxL7 17 我正在尝试提高 Bo
  • C++ 模板参数数量错误(2,应该是 1)

    我使用 C 并行快速排序程序进行了测试 如下所示 首先使用列表作为容器 然后我转移到通用容器类型 但它报告了标题错误 可以帮忙解决这个问题吗 include
  • 使用“const cv::Mat &”、“cv::Mat &”、“cv::Mat”或“const cv::Mat”作为函数参数的区别?

    我已经彻底搜索过 但没有找到一个简单的答案 传递 opencv 矩阵 cv Mat 作为函数的参数 我们传递一个智能指针 我们对函数内部的输入矩阵所做的任何更改也会改变函数范围之外的矩阵 我读到 通过将矩阵作为 const 引用传递 它不会
  • 从 AuthorizeAttribute 继承的属性不起作用

    我目前正在尝试根据用户角色在新的 ASP MVC 5 应用程序中实现安全性 目标是防止用户在没有特定角色 或更高角色 的情况下访问某些控制器或控制器方法 根据到目前为止我所读到的问题 我创建了一个继承 AuthorizeAttribute
  • 如何在 stl 模板中使用导出类 (__declspec(dllexport))?

    我正在使用导出的类 class declspec dllexport myclass private template declspec dllexport class std map
  • NSubstitute - 测试特定的 linq 表达式

    我在当前正在开发的 MVC 3 应用程序中使用存储库模式 我的存储库界面如下所示 public interface IRepository
  • 打印任何类型的数组和列表的通用方法[重复]

    这个问题在这里已经有答案了 每当我调试一段涉及整数 双精度 字符串等数组或列表的代码时 有时我更喜欢打印它们 我为此所做的是为不同类型编写重载的 printArray printList 方法 for e g 我可能有这 3 种方法来打印各
  • Selenium - 模式对话框存在 - 如何接受信息?

    我有以下问题 在页面上提交一些日期后 我有一个如图所示的模式对话框 我想单击 ENTER 来浏览该模式 但它不起作用 我有以下代码 driver FindElement By CssSelector input submit Click A

随机推荐