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

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

本文首先详细说明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.

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

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

函数回调在编程实现是一个特别重要的特性,它经常会在一些架构中使用到。而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的各种基本应用场景,并结合了例子进行说明,相信应该已经说明白了。

JavaScript 最常用函数(备忘查询)

JavaScript 是一种轻量级的解释型编程语言。

  • JSON 备忘清单
  • JavaScript 中的正则表达式
  • TypeScript 备忘清单

debugger 语句调用任何可用的调试功能。

== 只检查值,=== 检查值和类型。

从 ES2015 开始提供箭头函数

{ } 块内声明的变量

ES6 引入了两个重要的新 JavaScript 关键字:let 和 const。这两个关键字在 JavaScript 中提供了块作用域。

var 的范围是最近的函数块,而 let 的范围是最近的封闭块。

变量使用 let 有自己的副本,变量有使用 var 的共享副本。

将项目添加到末尾并返回新的数组长度。

末尾删除一个项目并返回已删除的项目。

从头删除一个项目并返回已删除的项目。

如果你想避免改变你的原始数组,你可以使用 concat。

将项目添加到开头并返回新的数组长度。

从 alert(num) 的值可以看出,continue outPoint; 语句的作用是跳出当前循环,并跳转到 outPoint(标签)下的 for 循环继续执行。

  • 指定隐式绑定:必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 将以上调用赋值给一个变量,结果最终会是 Window
  • 在 a 被调用的位置没有进行过任何显示绑定,最终全局对象 window 会调用它(Window.a)

call 第一个参数接受 this 作用域,剩余参数传递给其调用的函数

apply 第一个参数与 call 相同,第二个参数是其调用函数的参数数组

bind 函数会返回一个新函数

数组中的一些方法,类似于 map、forEach 等,可以自己设置绑定 this

其中一些全局对象,如 setTimeout 等,它们和未显示绑定 this 的部分数组方法一样,都会指向全局对象(Window)

创建 promises

另见:JSON 备忘单

XMLHttpRequest 是一个浏览器级别的 API,它使客户端能够通过 JavaScript 编写数据传输脚本,而不是 JavaScript 语言的一部分。

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。