0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

引入仿函数(functor)原因

Q4MP_gh_c472c21 来源:dnbc66 作者:dnbc66 2020-11-16 16:49 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

【导读】:在我们日常编码中会发现有些功能代码,会不断的在不同的成员函数中用到,但是又不好将这些代码独立成一个成员函数。解决办法之一就是写一个公共的函数,不过函数用到的一些变量,就可能会成为全局变量。再说为了复用这么一段代码,就要单立出一个函数,也不是很好维护。此时就可以用到仿函数了。

以下是正文

引入仿函数(functor)原因

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

bool LengthIsLessThanFive(const string& str){returnstr.length()< 5;    }int res=count_if(vec.begin(), vec.end(), LengthIsLessThanFive);

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

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

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

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

(1)函数的局部变量:

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

(2)函数的参数:

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

(3)全局变量:

我们可以将长度阈值设置成一个全局变量,代码可能像这样:

int maxLength;bool LengthIsLessThan(const string& str) { return str.length() < maxLength;}int res=count_if(vec.begiin(), vec.end(), LengthIsLessThan);

这段代码看似很不错,实则不符合规范,更重要的是,它不优雅。原因有以下几点要考虑:

(1)容易出错:

为什么这么说呢,我们必须先初始化maxLength的值,才能继续接下来的工作,如果我们忘了,则可能无法得到正确答案。此外,变量maxLength和函数LengthIsLessThan之间是没有必然联系的,编译器无法确定在调用该函数前是否将变量初始化,给码农平添负担。

(2)没有可扩展性:

如果我们每遇到一个类似的问题就新建一个全局变量,尤其是多人合作写代码时,很容易引起命名空间污染(namespace polution)的问题;当范围域内有多个变量时,我们用到的可能不是我们想要的那个。

(3)全局变量的问题:

每当新建一个全局变量,即使是为了coding的便利,我们也要知道我们应该尽可能的少使用全局变量,因为它的cost很高;而且可能暗示你这里有一些待解决的优化方案。

仿函数(functor)介绍

说了这么多,还是要回到我们原始的那个问题,有什么解决方案呢?答案当然就是这篇blog的正题部分:仿函数。

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

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

class Func{ public: void operator() (const string& str) const { cout<

>>>helloworld!

仿函数其实是上述解决方案中的第四种方案:成员变量。成员函数可以很自然的访问成员变量:

class StringAppend{ public: explicit StringAppend(const string& str) : ss(str){} void operator() (const string& str) const{ cout<

>>>hellois world

我相信这个例子能让你体会到一点点仿函数的作用了;它既能像普通函数一样传入给定数量的参数,还能存储或者处理更多我们需要的有用信息。

让我们回到count_if的问题中去,是不是觉得问题变得豁然开朗了?

class ShorterThan { public: explicit ShorterThan(int maxLength) : length(maxLength) {} bool operator() (const string& str) const { return str.length() < length; } private: const int length;};//直接调用即可count_if(myVector.begin(), myVector.end(), ShorterThan(length));

这里需要注意的是,不要纠结于语法问题:ShorterThan(length)似乎并没有调用operator()函数?其实它调用了,创建了一个临时对象。你也可以自己加一些输出语句看一看。

这篇博文就先记到这里了,仿函数也在STL中大量涉及到,不彻底弄懂仿函数的问题看到STL源码就会一头包。后续可能再分享一些关于functor的资料和个人学习心得。

责任编辑:lq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4422

    浏览量

    67853
  • C++
    C++
    +关注

    关注

    22

    文章

    2131

    浏览量

    77378
  • 代码
    +关注

    关注

    30

    文章

    4976

    浏览量

    74385

原文标题:C++仿函数你会吗?

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    嵌入式开发常用函数速查表

    在嵌入式开发中,掌握常用函数的用法可以大大提高开发效率。无论是单片机初学者还是有一定经验的工程师,熟悉函数库和调用方式都是必备技能。今天,我们为大家整理一份嵌入式开发常用函数速查表,覆盖GPIO
    的头像 发表于 01-19 09:06 469次阅读
    嵌入式开发常用<b class='flag-5'>函数</b>速查表

    仿出现X态的原因

    1、仿真pattern自身原因,比如程序使用了未初始化(写)的存储区,读出红X的数据并使用,导致红X传播; 2、仿真环境或者平台原因,模型或者整chip顶层PIN脚的信号没有驱动,是高阻Z态,进入
    发表于 01-16 06:01

    如何用好 C 语言函数的返回值?

    myopen(),用于打开某个文件,那么,这个函数要么能够成功打开文件,要么打开文件失败,这时,可以通过返回值区分“成功”和“失败”。 当然,myopen() 函数失败的原因可能很多,但不是本文关注
    发表于 12-25 07:17

    使用setjmp及longjmp函数处理异常

    是一个用于传递跳转原因的整数值,它不能为0。setjmp函数用于设置跳转点,并返回0;longjmp函数用于跳转到跳转点,并使setjmp函数返回val。例如: #include
    发表于 12-11 08:00

    硬件问题造成的MCU死机的原因

    答复给客户芯片不良的原因。 虽然,但是! 不过有些问题确实是静电导致的。这时,我们可以通过静电枪在敏感位置进行接触放电测试模拟死机现象。因为静电引入导致的死机问题,我们只需按照解ESD的思路进行解决即可
    发表于 11-24 08:07

    激活函数ReLU的理解与总结

    容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron)了,那么网络的逼近能力就相当有限。正因为上面的原因,需要引入非线性函数
    发表于 10-31 06:16

    使用函数块实现三相电机正反转控制

    在使用西门子S1200PLC,所使用的软件是博途软件,在这个软件里运用了块的概念。比如我们常见的组织块(OB)、函数块(FB)、数据块(DB)以及函数FC等。今天我们来具体交流一下这个函数块(FB)的具体使用方法。
    的头像 发表于 10-15 14:40 2920次阅读
    使用<b class='flag-5'>函数</b>块实现三相电机正反转控制

    在标准库下配置的stm32f103x的spi在引入rtthread后收发的消息就错了,为什么?

    我在标准库下配置的stm32f103x的spi在引入rtthread后收发的消息就错了,引入之前没问题,请问这是啥情况
    发表于 10-13 08:07

    电压暂降的原因有哪些?

    电压暂降的原因可归纳为 电网侧故障、负荷侧扰动、外部环境影响 三大类,其中电网侧短路故障和负荷侧冲击性负荷启动是最主要诱因,两者合计占所有暂降事件的 80% 以上。不同原因的发生场景、影响机制及频率
    的头像 发表于 10-11 17:23 4438次阅读
    电压暂降的<b class='flag-5'>原因</b>有哪些?

    线程超时函数中 assert 失败是什么原因导致的?

    最近调试 gd32h759 遇到了一个十分奇怪的问题,在初步调通所有的逻辑功能后,发现系统经常会在运行一段时间后死在一个奇怪的线程超时函数中 assert 失败导致卡死。用 cmbacktrace
    发表于 09-09 06:56

    德国莫尔利用全新的在线配置器改进了电缆引入装置设计流程

    在线产品,为定制电缆引入装置设定了新的基准。murrplastik高级产品经理Till Bergmann表示:莫尔是完全根据客户要求生产多种结构电缆引入装置的公司,特别是机器人和能源链应用领域
    发表于 08-20 14:29

    详解hal_entry入口函数

    当使用RTOS时,程序从main函数开始进行线程调度;当没有使用RTOS时,C语言程序的入口函数main函数调用了hal_entry函数。由于我们新建的工程是没有选用RTOS的,因此,
    的头像 发表于 07-25 15:34 2229次阅读

    C语言中的内联函数与宏

    在C编程中,内联函数和宏都用于避免函数调用的开销并编写可复用的逻辑部分,但它们在工作方式和安全性方面存在显著差异。
    的头像 发表于 07-25 15:10 2072次阅读
    C语言中的内联<b class='flag-5'>函数</b>与宏

    迅为RK3568开发板驱动指南GPIO子系统GPIO子系统API函数引入

    迅为RK3568开发板驱动指南GPIO子系统GPIO子系统API函数引入
    的头像 发表于 05-29 14:05 1194次阅读
    迅为RK3568开发板驱动指南GPIO子系统GPIO子系统API<b class='flag-5'>函数</b>的<b class='flag-5'>引入</b>

    LTspice里压敏电阻MOV怎么引入?

    LTspice里压敏电阻MOV怎么引入
    发表于 04-28 08:26