C++技能系列 ( 6 ) - 可调用对象、std::function与std::bind【详解】

系列文章目录

C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程

期待你的关注哦!!!
在这里插入图片描述

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

一、可调用对象

1、函数指针

调用函数,就是一个可调用对象。

看如下范例:

//myfunc函数的定义
void myfunc(int tv)
{
	cout << "myfunc()函数执行了, tv = " << tv << endl;
}

//在main主函数中,加入如下代码
void (*pmf)(int) = myfunc; // 定义函数指针并给初值,myfunc也可以写成&myfunc,是一样的。
pmf(15);//调用函数,就是一个可调用对象

2、具有operator( )成员函数的类对象(仿函数或函数对象)

仿函数的定义:仿函数(functors)又称为函数对象(function objects),是一个能行驶函数功能的类所定义的对象。仿函数的语法几乎和普通函数调用一样。

看如下范例:

class TC
{
public:
	void operator()(int tv)
	{
		cout << "TC::operator()执行了,tv= " << tv << endl;
	}
};
//在mian主函数中,加入如下代码
TC tc;
tc(20);//调用的是( )操作符,这就是一个可调用对象,等价于tc.operator()(20);

3、可被转换为函数指针的类对象

可被转换为函数指针的类对象也可以叫作仿函数或函数对象。

看如下范例:

class TC2
{
public:
	using tfpoint = void(*)(int);
	static void myfunc(int tv) //静态成员函数
	{
		cout << "TC2::mysfunc()静态成员函数执行了,tv=" << tv << endl;
	}
	operator tfpoint() //类型转换运算符/类型转换函数
	{
		return mysfunc;
	}
}
//在mian主函数中,加入如下代码
TC2 tc2
tc2(50);//先调用tfpoint,再调用mysfunc;这就是一个可调用对象,等价于tc2.operator TC2::tfpoint()(50);

4、类成员函数指针

看如下范例:

class TC
{
public:
	void operator()(int tv)
	{
		cout << "TC::operator()执行了,tv= " << tv << endl;
	}
	void ptfunc(int tv){
		cout << "TC::ptfunc()执行了,tv = " << tv << endl;
	}
private:
	int m_a;
};
//在mian主函数中,加入如下代码
TC tc3;
//类成员函数指针变量myfpoint定义并给初值
void (TC::* myfpointpt)(int) = &TC::ptfunc; 
//要调用成员函数,就必须用到对象tc3
(tc3.*myfpointpt)(68);

那么,有没有什么方法能够把这些可调用对象的调用形式统一一下呢?有,那就是使用std::function把这些可调用对象包装起来。

二、std::function可调用对象包装器

std::function的头文件是#include< functional > ,这个类模版用来包装各种可调用对象,比较遗憾的是它不能装类成员函数指针,因为类成员函数指针是需要类对象参与才能完成的。

std::function类模版的特点是:通过指定模版参数,它能够用统一的方式来处理各种可调用对象。

1、绑定普通函数

看如下范例:

//绑定一个普通函数,注意< >中的格式
std::function<void(int)> f1 = myfunc;
//调用普通函数
f1(100);

2、绑定类的静态成员函数

看如下范例:(在TC类增加一个public修饰的静态成员函数)

class TC
{
public:
	void operator()(int tv)
	{
		cout << "TC::operator()执行了,tv= " << tv << endl;
	}
	static int stcfunc(int tv)
	{
		cout << "TC::stcfunc()静态成员函数执行了,tv = " << tv << endl;
		return tv;
	}
};
//在mian主函数中,加入如下代码
//绑定一个类的静态成员函数
std::function<int(int)> fs2 = TC::stcfunc;
//调用静态成员函数
fs(110);

3、绑定仿函数

class TC
{
public:
	TC() //构造函数
	{
		m_a = 1;
	}
	void operator()(int tv)
	{
		cout << "TC::operator()执行了,tv= " << tv << endl;
	}
privateint m_a;
};
//在mian主函数中,加入如下代码
TC tc3;
std::function<void(int)> f3 = tc3; 
f3(120); //TC::operator()执行了,tv = 120

4、范例演示

范例一:

class CB
{
	std::function<void()>fcllback;
public:
	CB(const std::function<void()> &f):fcallback(f)
	{
		int i;
		i = 1;
	}
	void runcallback(void)
	{
		fcallback();
	}
};

class CT
{
public:
	CT()
	{
		cout << "CT::CT()执行" << endl;
	}
	CT(const CT&) //拷贝构造函数
	{
		cout << "CT::CT(const CT&)执行" << endl;
	}
	void operator()(void)
	{
		cout << "CT::operator()执行" << endl;
	}
};

int main()
{
	CT ct;
	CB cb(ct);
	cb.runcallback();
	
	return 0;
}

范例二:

void mycbk(int cs, const std::function<void(int)>& f)
{
	f(cs);
}
void runfunc(int x)
{
	cout << x <<endl;
}
int main()
{
	for(int i = 0; i < 10; i ++)
	{
		mycbk(i, runfunc);
	}
	return 0;
}

三、std::bind 绑定器

std::bind是一个函数模版,这个函数模版的头文件#include< functional >std::bind能将对象以及相关的参数绑定到一起,绑定完后可以直接调用,也可以用std::function进行保存,在需要的时候调用。该函数的一般格式如下:
std::bind(带绑定的函数对象/函数指针/成员函数指针, 参数绑定值1, 参数绑定值2, ..., 参数绑定值n);

std::bind有两个意思:

- 将可调用对象和参数绑定在一起,构成一个仿函数,所以可以直接调用。
- 如果函数有多个参数,可以绑定部分参数,其他的参数在调用的时候指定。

下面我们通过范例来理解这个函数模版的使用:

详解看备注


void myfunc1(int x, int y, int z)
{
	cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
}

void myfunc2(int &x, int &y)
{
	x++;
	y++;
}

class CQ
{
public:
	void myfunpt(int x, int y)
	{
		cout << "x=" << x << ",y=" << y << endl;
		m_a = x;
	}
private:
	int m_a = 0; 
}

int main()
//在main主函数
{
	//😄💪🏻使用方式一
	{
		//其实返回值是一个仿函数对象,可以直接调用,也可以赋给std::function
		auto bf1 = std::bind(myfunc1, 10, 20, 30);
		//执行myfunc1函数,结果:x=10,y=20,z=30
		bf1();
	}
	//😄💪🏻使用方式二
	{
		//也可以使用placeholders::占位符
		auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30);
		bf2(5, 15);//结果:x=5,y=15,z=30
	}
	//😄💪🏻使用方式三
	{
		//也可以直接调用
		std::bind(myfunc1, placeholders::_1, placeholders::_2, 30)(10, 20);//x=10,y=20,z=30
	}
	//😄💪🏻使用方式四
	{
		//调换参数
		auto bf3 = std::bind(myfunc1, placeholders::_2, placeholders::_1, 30);
		bf2(5, 15); //结果:x=15,y=5,z=30
	}
	//😄💪🏻使用方式五
	{
		int a = 2;
		int b = 3;
		auto bf4 = std::bind(myfunc2, a, placeholders::_1);
		bf4(b); //执行后结果:a=2,b=4。
				//这说明:bind对于预先绑定的函数的参数是通过值传递的所以这个a是值传递的。
				//bind对于不事先绑定的参数,通过std::placeholders传递的参数是
				//通过引用传递的,所以这个b实际上是引用传递的
	}
	//😄💪🏻使用方式六
	{
		//一个类对象
		CQ cq;
		//类函数有绝对地址,和对象无关,但要被调用必须有类对象参数
		auto bf5 = std::bind(&CQ::myfunpt, cq, placeholders::_1, placeholders::_2);
		//对成员函数的调用
		bf5(10, 20); //结果为:x=10,y=20
		
		//⚠️上面的的第二个参数cq会导致生成一个临时的CQ对象,修改其m_a的值是不会改变真实cq对象,
		//⚠️cq前面加&,这样就不会导致生成一个临时的CQ对象,可以改变m_a的值
		//⚠️auto bf5 = std::bind(&CQ::myfunpt, &cq, placeholders::_1, placeholders::_2);
	}
	//😄💪🏻使用方式七
	{
		//bind和function配合使用(bind返回值直接赋给std::function类型)
		std::function<void(int, int)> bfc6 = std::bind(&CQ::myfunpt, cq, placeholders::_1, placeholders::_2);
		bfc6(10, 20);
	}
	//😄💪🏻使用方式八
	{
		//绑定成员变量
		std::function<int&(void)>bf7 = std::bind(%CQ::m_a, &cq);
		bf7() = 60; //执行后cq对象的m_a的成员变量值变为了60了
	}
	
	return 0;
}


四、小结

因为有了占位符(placeholder)这种概念,所以std::bind使用变得更加灵活。

std::bind 也可以绑定部分参数,绑定部分参数时,就需要通过std::placeholder来决定bind所在的位置的参数将会属于调用发生时的第几个参数。

  • std::bind的思想实际上就是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候调用。
  • std::function一般要绑定一个可调用对象,类成员函数不能被绑定。而std::bind更加强大,成员函数、成员变量等都能绑定。现在通过std::functionstd::bind配合使用,所有的可调用对象都有了统一的操作方法。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Allen.Su

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

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

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

打赏作者

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

抵扣说明:

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

余额充值