百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

没想到bind的功能这么强大,赶紧来看看,助你掌握新技能

myzbx 2025-06-23 20:54 4 浏览

std::bind是C++11中一个函数模版,就像函数适配器,接受一个可调用对象(callable object),生成一个新的可调用对象。通过它,我们可以实现类似传统的函数指针,函数回调等功能,并且能够降低代码的复杂度。

本文首先详细说明std::bind的基本用法以及解释使用过程中疑问点,然后再介绍如何利用传统函数指针搭建基础架构,再说明如何用std::bind来代替函数指针,最后介绍如何用std::bind来实现函数回调的功能。



一、std::bind的基本用法

首先看下std::function, 它就是std::bind返回的新的可调用对象。如下图,定义实现了普通加法函数Add, 然后将该函数指针赋值给std::function类型的变量,这里可以注意到,使用了Add和&Add进行赋值。两者是等效的,这是因为使用Add的时候,会隐式转换成函数指针。

运行程序之后的输出信息,可以看出std::function类型的变量的使用与普通函数的使用是一样的。


我们不直接采用普通函数对std::function进行赋值,而是采用stb::bind,首先看下简单的实例,其中std::placeholders::_1和std::placeholders::_2是占位符,代表函数的入参。如果调用的时候,需要传递具体实参,那么就需要使用placeholders来占位。这里需要注意std::placeholders::_1并不是代表函数的第一个入参数,至于为什么,请继续往下阅读,下面将会通过实例进行阐述。

如果函数的第二个入参是一个固定值,那么第一个入参就需要使用占位符std::placeholders::_1,如下所示,函数第二个参数固定位数值5,那么使用std::function类型变量的时候,也只需要传递一个参数,该参数代表Add函数的第一个参数。


如果Add函数的第一个入参是一个固定值,那么第二个入参就需要使用占位符std::placeholders::_1(注意不是std::placeholders::_2),如下所示,函数第一个参数固定位数值6,那么使用std::function类型变量的时候,也只需要传递一个参数,该参数代表Add函数的第二个参数。

当然,如果函数Add的两个参数都是固定值,那么使用std::function类型变量的时候,就不需要参数了。

这里有个小技巧,如果不想要书写std::function那么繁琐的信息表示,那么可以采用auto代替,但是注意不要滥用auto.

二、std::bind的扩展

上面说明的是stb::bind使用普通函数的方法,那么如果是类的成员函数呢?应该如何使用呢?首先s td::bind的第一个参数是类成员函数指针,第二个参数为类对象的指针,其他的用法与使用普通函数的用法是一样的。

为了说明std::bind参数值是默认按照值传递的,首先实现函数Print, 该函数的入参是一个引用,函数内部将参数自增1,然后输出打印信息。接着再通过输出std::bind使用前后日志信息来确认是否是按照值传递。

从输入的打印信息看,std::bind使用前后的信息没有发生变化,说明std::bind是默认按照值传递的。


如果想要按照引用来传递变量,应该如何操作呢,那么就是std::ref登场的时候,std::ref是用于包装引用传递的值。


从输出打印信息看,采用std::ref传递变量之后,std::bind使用前后的信息发生变化了。


另外补充一点,std::cref用于包装const引用传递的值。


三、传统函数指针

函数指针变量用于存储函数指针,以便后续的调用。有时候可以利用它实现多个消息对象的处理,并且一定程度满足开闭原则。

首先实现抽象基类JAbstractBaseTest,接着再实现继承JAbstractBaseTest的两个子类JObjA和JObjB


完成上面的测试类,接着实现基础的框架,定义函数指针CreateObj,该函数指针用于动态创建对象,然后再分别实现初始化创建对象的函数指针映射表以及通过id从映射表中获取函数对象的两个函数。

使用调用方式如下,通过id获取对象指针,然后执行对象的run函数。通过这样的方式,可以做到主体循环不变,如果需要添加新的对象处理,那么只要实现新的类,然后添加到映射表中即可。


四、std::bind代替函数指针

std::bind和std::function的结合,可以实现函数指针的功能。通过using Funtor = std::function<void (void)>来实现类似函数指针的声明。其中Funtor表示std::function<void (void)>的别名。然后在初始化表函数InitTab中,通过使用std::bind将类的函数成员一一映射到map中。



调用JDebugBind的方式如下,只需要传递函数的id给函数RunTest,即可执行到对应的函数。同样的,后续如果想要添加新的功能,那么只要实现新的函数,并且将其添加到map中即可。


五、std::bind实现函数回调

函数回调在编程实现是一个特别重要的特性,它经常会在一些架构中使用到。而std::bind是可以实现函数回调的特性的。下图实现的类JDebugCallback中,构造函数接受一个类型为std::function的参数之后,将其赋值给类的成员函数m_callback,后续调用函数Start的时候,Start函数内部再调用m_callback,从而实现函数回调。这里只是一个简单的例子说明,可能还不能充分看到函数回调的强大。希望这里作为一个引入,后续在实际工作中,再慢慢的体会。



最后看下怎么使用JDebugCallback类,实现类两个函数CallBack01和CallBack02,然后通过std::bind传递给JDebugCallback,接着JDebugCallback对象调用Start来执行传递进来的函数。


五、总结

至此,C++11提供的std::bind的用法和扩展已经介绍完毕,虽然工作中有各种各样的需求场景,但是只要掌握了知识的基本原理,就能够以不变应万变。本文介绍了std::bind的各种基本应用场景,并结合了例子进行说明,相信应该已经说明白了。

相关推荐

轻松!午休十分钟搞懂 Vue 组件 data 为啥是函数,悄悄涨知识

上午敲代码敲得肩膀发酸?来,端起咖啡,咱在午休时间唠点轻松的——一道让无数前端人又爱又恨的Vue面试题:为啥组件里的data必须是个函数,写成对象就报错?别慌,这事儿没你想的复杂,就像拆...

你知道吗,python编程中,有哪些数据类型是不可变的?

在Python中,不可变数据类型是指对象一旦创建后,其值或内容不能被修改。若尝试修改,Python会创建一个新对象。以下是不可变数据类型的详细分类及验证方法:一、不可变数据类型清单1.整数(...

12 有符号Byte数据类型(1byte有符号整数取值范围)

有符号Byte数据类型。来看一下有符号Byte数据。如果需要同时存储、正值和复制,那就需要有一个符号位来表示它是正数还是负数,Byte数据类型是这样的数据类型,它是有八位表示,最高位是符号位,它表示的...

Redis数据类型介绍(redis数据类型详解)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

C语言的数据类型(c语言的数据类型有哪些)

C语言的数据类型在C语言中,数据类型用于定义变量存储的数据种类和大小,主要分为以下几类:1.基本数据类型(PrimaryDataTypes)(1)整数类型类型存储大小(通常)取值范围说明cha...

Java基础数据类型与核心概念(java基础数据类型口诀)

一、Java基础数据类型与核心概念1.八大基础数据类型及其包装类基础类型:byte、short、int、long、float、double、char、boolean。包装类:Byte、Short、I...

没想到bind的功能这么强大,赶紧来看看,助你掌握新技能

std::bind是C++11中一个函数模版,就像函数适配器,接受一个可调用对象(callableobject),生成一个新的可调用对象。通过它,我们可以实现类似传统的函数指针,函数回调等功能,并且...

C#数据结构 串(c语言数据结构串)

串是一种数据元素为字符的特殊的线性表。1.串的定义零个或多个字符(字母、数字或其他字符)组成的有限序列。记为S="a1a2...an"S="a1a2...an",长...

2025-05-14:统计能获胜的出招序列数。用go语言,Alice 和 Bob 玩一

2025-05-14:统计能获胜的出招序列数。用go语言,Alice和Bob玩一个回合制幻想战斗游戏,游戏共进行n轮。每轮双方同时召唤一种魔法生物,三种生物分别是火龙(F)、水蛇(W)和地精...

vue 基础- nextTick 的使用场景(vuenexttick的原理浅析简书)

前言《vue基础》系列是再次回炉vue记的笔记,除了官网那部分知识点外,还会加入自己的一些理解。(里面会有部分和官网相同的文案,有经验的同学择感兴趣的阅读)在开发时,是不是遇到过这样的场景,响应...

FANUC 0iF 用户宏程序——用户宏程序调用(自变量赋值)

自变量的赋值又2种方式:自变量赋值I和自变量赋值Ⅱ。第I类使用G、L、O、N、P以外的字母,每个用一次;第Ⅱ用A、B、C,每个用一次,还可使用10组I、J、K,自变量的指定种类是根据所用的字母自动决定...

为什么 JS 开发者更喜欢 Axios 而不是 Fetch?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!本文我会解释为什么在开发中Axios是...

JVM- 类的加载过程、类加载器,看这就够了。

一、类的加载过程类从加载到内存中开始,到卸载出内存位置,为类的生命周期。包括加载(loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始...

解锁ES6宝藏:用这些新特性让你的JavaScript代码更优雅!

各位技术爱好者们!有没有觉得,JavaScript这门语言更新迭代的速度快得惊人?仿佛一眨眼,就又冒出了好多新概念、新语法。特别是从2015年开始,JavaScript迎来了一次里程碑式的升级——ES...

C语言 | 将一个二维数组行列元素互换

例24:C语言实现将一个二维数组行和列的元素互换,存到另一个二维数组中。例如:a数组的序列:     123   ...