C++ 几种可调用对象的封装与调用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言


一、可调用对象是什么?

C++中有如下几种可调用对象:函数、函数指针、lambda表达式、bind对象、函数对象类。
C++11通过提供std::function 和 std::bind统一了可调用对象的各种操作。

std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。它比普通函数指针更加的灵活和便利。

std::bind
std::bind可以看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。
std::bind主要有以下两个作用:
将可调用对象和其参数绑定成一个仿函数;
只绑定部分参数,减少可调用对象传入的参数。

二、c++可调用对象

函数

int print(int a, double b)
{
    cout << a << b << endl;
    return 0;
}

函数指针

typedef void (*func)(int, double) ;

int (*func)(int, double) = &print;

lambda表达式
就是一段可调用的代码。主要适合于只用到一两次的简短代码段。由于lambda是匿名的,所以保证了其不会被不安全的访问.
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
Lambda 主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable 或 exception 声明、-> 返回值类型、{函数体}.
捕捉列表:该列表出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表可以捕捉上下文中的变量供lambda函数使用
参数列表:与普通函数的参数列表一致。则可以连同()一起省略
mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符,参数列表不可以省略(即使参数列表为空)->返回值类型。用于追踪返回值类型。没有返回值时可以省略。返回值类型明确的情况下,也可以省略
{函数体}:在该函数体,除了可以使用参数外,也可以使用捕捉到的所有变量
参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空

a [var]:表示值传递方式捕获变量var
b [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
c [&var]:表示引用传递变量var
d [&]:表示引用传递捕获所有父作用域中的变量(this)
e [this]:表示值传递方式捕获当前的this指针

auto func = [&](int a, double b)->int{
    cout << a << b << endl;
    return 0;
}

bind对象
std::bind可以将可调用对象和参数一起绑定,绑定后的结果使用std::function进行保存,并延迟调用到任何我们需要的时候。
std::bind主要有以下两个作用:
将可调用对象和其参数绑定成一个仿函数;
只绑定部分参数,减少可调用对象传入的参数。
预绑定的参数是以值传递的形式,不预绑定的参数要用std::placeholders(占位符)的形式占位,从_1开始,依次递增,是以引用传递的形式;
std::placeholders表示新的可调用对象的第几个参数,而且与原函数的该占位符所在位置的进行匹配;

int print(int a, double b){
    cout << a << b << endl;
    return 0;
}
auto bindPrint = bind(print, std::placeholders::_1, std::placeholders::_2);

必须显示的指定&A::print,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在A::print前添加&
使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &a;
std::placeholders::_1, std::placeholders::_2表示占位符

class A{
public:
	int print(int a, double b){
	    cout << a << b << endl;
	    return 0;
	}
};
void callA()
{
	A a;
	auto bindPrint = bind(&A::print, &a,std::placeholders::_1, std::placeholders::_2);
}

函数对象类
是一个具有operator()成员函数的类对象(仿函数)

struct A{
{
Public:
    // ()操作符重载
    void operator()(int a, double b){
        cout << a << b << endl;
    }
};
void callA()
{
	A a;
    a(1010.0);	// 仿函数
}

三、c++可调用对象的使用

class CallBack
{
public:
  void registerCallBack(std::function<void()> funcb){
    _callback = std::move(funcb);
  } 
  void callCB(){
  	_callback();
  }
private:
  std::function<void()> _callback; 
}

void callCB()
{
	CallBack CB;
	CB.registerCallBack([](){
		std::cout<< "this is callback" <<std::endl;
	}
	);
	CB.callCB();
}
#include <iostream>
#include <functional>
typedef std::function<int(int, int)> mfun;
// 普通函数
int add(int a, int b) { return a + b; }
// lambda表达式
auto mod = [](int a, int b){ return a % b; };
// 函数对象类
struct divide{
    int operator()(int denominator, int divisor){
        return denominator/divisor;
    }
};
//函数对象类
class SUB{
public:
	int sub(int a, int b)
	    return a-b;
	}
};

int main(){
	SUB sub;
	mfun a = add;
	mfun b = mod;
	mfun c = divide();
	mfun d = std::bind(&SUB::sub, &sub,std::placeholders::_1, std::placeholders::_2);
    std::cout << a(5, 3) << std::endl;
    std::cout << b(5, 3) << std::endl;
    std::cout << c(5, 3) << std::endl;
    std::cout << d(5, 3) << std::endl;
}

四、补充知识点

为什么C++11引入了std::ref,std::cref
C++本身有引用(&),为什么C++11又引入了std::ref?
主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用。
bind()是一个函数模板,它的原理是根据已有的模板,生成一个函数,但是由于bind()不知道生成的函数执行的时候,传递进来的参数是否还有效。所以它选择参数值传递而不是引用传递。如果想引用传递,std::ref 引用传递,允许修改和std::cref const引用传递,函数内部不能修改。

std::thread的构造函数只会单纯的复制传入的变量,特别需要注意的是传递引用时,传入的是值的副本,也就是说子线程中的修改影响不了主线程中的值。即使是用引用来接收传的值,也是会将其拷贝一份到子线程的独立内存中,这一点与我们编写普通程序时不同。

因为线程的创建属于函数式编程,所以为了传引用C++中才引入了std::ref()。此时需要使用std::ref()。但是注意如果我们会在子线中改变它,此时用于接收ref()的那个参数前不能加const

仿函数是什么
仿函数(functors)在C++标准中采用的名称是函数对象(function objects),仿函数本质就是类重载了一个operator(),创建一个行为类似函数的对象。
对于重载了()操作符的类,可以实现类似函数调用的过程,所以叫做仿函数,实际上仿函数对象仅仅占用1字节,因为内部没有数据成员,仅仅是一个重载的方法而已。

struct MyPlus{
    int operator()(const int &a , const int &b) const{
        return a + b;
    }
};

//1、通过产生临时对象调用重载运算符
//2、通过对象显示调用重载运算符
//3、通过对象类似函数调用 隐示地调用重载运算符
int main(){
    MyPlus a;
    cout << MyPlus()(1,2) << endl;     
    cout << a.operator()(1,2) << endl;  
    cout << a(1,2) << endl;            
    return 0;
}


总结

通过本文的学习,你应该对std::function 和 std::bind有了一定的认识

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

c+猿辅导

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

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

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

打赏作者

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

抵扣说明:

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

余额充值