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

news2024/10/6 18:31:03

系列文章目录

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

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

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

可调用对象、std::function与std::bind

  • 系列文章目录
  • 一、可调用对象
    • 1、函数指针
    • 2、具有operator( )成员函数的类对象(仿函数或函数对象)
    • 3、可被转换为函数指针的类对象
    • 4、类成员函数指针
  • 二、std::function可调用对象包装器
    • 1、绑定普通函数
    • 2、绑定类的静态成员函数
    • 3、绑定仿函数
    • 4、范例演示
  • 三、std::bind 绑定器
  • 四、小结

一、可调用对象

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配合使用,所有的可调用对象都有了统一的操作方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/668739.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单片机如何生成周期正弦波

一&#xff0c;简介 在某些场景需要使用单片机的IIS等外设播放正弦波音频数据。本文介绍一种“笨方法”来生成固定频率和固定幅度的正弦波定点型数据&#xff0c;记录总结学习使用。 二&#xff0c;步骤简介 总体步骤概述&#xff1a; 1&#xff0c;使用Audition生成制定波形…

chatgpt赋能python:Python异常捕获存在的问题

Python 异常捕获存在的问题 作为一门广受欢迎、应用广泛的编程语言&#xff0c;Python 在处理异常方面有着比较完善的设计。Python 提供了 try…except…finally 这样的异常处理机制&#xff0c;通过这些机制&#xff0c;开发者可以捕获、处理程序中产生的异常&#xff0c;从而…

【安全】awvs安装(一)

目录 一、简介 二、将安装文件传输到服务器 三、安装 3.1 赋权 3.2 执行安装 四、激活 4.1 复制激活文件到对应安装目录 4.2 赋权 4.3 运行激活文件 五、访问 六、设置不自动更新 七、设置开启 八、忘记密码 前言&#xff1a;安全漏洞扫描工具awvs的安装使用 一、简介…

Android DiskLruCache完全解析,硬盘缓存的最佳方案

概述 LruCache只是管理了内存中图片的存储与释放&#xff0c;如果图片从内存中被移除的话&#xff0c;那么又需要从网络上重新加载一次图片&#xff0c;这显然非常耗时。对此&#xff0c;Google又提供了一套硬盘缓存的解决方案&#xff1a;DiskLruCache(非Google官方编写&…

Nginx基础配置

Nginx的基础配置&#xff1a; Nginx的基础配置 一、实战案例&#xff1a;1.Nginx访问统计&#xff1a;2.基于授权的访问控制&#xff1a;3.基于客户端访问设置&#xff08;设置黑白名单&#xff09;&#xff1a;4.基于域名的nginx虚拟主机&#xff1a;5.基于IP的nginx虚拟主机…

算法分析02--分治法

3.分治法 3.1递归 递归是指子程序&#xff08;或函数&#xff09;直接调用自己或通过一系列调用语句间接调用自己&#xff0c; 是一种描述问题和解决问题的常用方法。使用递归技术往往使函数的定义和算法的描述简洁且易千理解。 递归有两个基本要素&#xff1a;边界条件&…

告别固定字体大小:CSS使用相对单位提升网页可访问性和兼容性

在 Web 开发领域中&#xff0c;有很多误解流传&#xff0c;即使它们被反驳了很多次也仍然存在。"外部链接应该总是在新标签页中打开" 就是一个很好的例子。CSS Tricks 在将近十年前就对此进行了详细的解释&#xff08;简而言之&#xff1a;大多数情况下是错误的&…

沃尔玛、亚马逊跨境境电商如何实现自养账号采退、海淘及测评的

今天曹哥还是针对下亚马逊、沃尔玛跨境平台如何实现自己养买家号可以给自己采购、海淘及测评等技术问题 首先你要有一个稳定的环境方案&#xff0c;这个是做自养号采购、海淘及测评的基础。环境有很多&#xff0c;从早期的虚拟机&#xff0c;模拟机&#xff0c;云手机&#xf…

MATLAB | 绘图复刻(十) | 旋转45度的相关系数热图绘制

我们在论文里经常能见到这样三角形的相关性热图&#xff0c;这样的热图绘制出来会更节省空间&#xff1a; 这期就教大家如何绘制三角形的热图&#xff0c;绘制效果如下&#xff1a; 以下是教程部分&#xff1a; 0 数据准备 这里随便生成了一组数据&#xff0c;计算了相关系数&…

C++——vector迭代器失效与深浅拷贝问题

目录 1. vector迭代器失效问题 1.1 insert迭代器失效 1.1.1 扩容导致野指针 1.1.2 意义变了 1.1.3 官方库windows下VS和Linux下对insert迭代器失效的处理 1.2 erase迭代器失效 1.2.1 失效原因分析 1.2.2 官方库windows下VS和Linux下对erase迭代器失效的处理 1.2.3 测试…

0基础学习VR全景平台篇第46篇:底部菜单- 【开场地图】与【高清矩阵】的对比

大家好&#xff0c;欢迎观看蛙色VR官方——后台使用系列课程&#xff01; 这期 &#xff0c;我们将为大家讲解蛙色VR平台-【开场地图】与【高清矩阵】功能的区别 功能位置示意 一、功能具体应用 开场地图分为两种&#xff0c;分别是高德地图和手绘地图。 高德地图点位目前系统…

【Linux】MySQL 存储引擎

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 MySQL 存储引擎 MyISAM 表支持 3 种不同的存储格式常用存储引擎&#xff1a;InnoDB、MyISAM查看系统支持的存储引擎查看表使用的存储引擎修改存储引擎死锁如何尽可能避免死锁…

每日学术速递6.7

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.The ObjectFolder Benchmark: Multisensory Learning with Neural and Real Objects(CVPR 2023) 标题&#xff1a;ObjectFolder 基准测试&#xff1a;使用神经和真实对象进行多感官…

计算机网络管理- SNMP协议报文和报文格式分析,SNMP PDU分析

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

【玩转Linux操作】Linux服务管理

&#x1f38a;专栏【玩转Linux操作】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;服务(service)管理⭐service管理指令 &…

《网络安全0-100》网络安全技术

网络安全技术 防火墙 入侵检测系统 防火墙(Firewall)是一种网络安全设备&#xff0c;用于监控和控制网络流量&#xff0c;以保护网络免受未经授权的访问和攻击。防火墙通过过滤网络流量&#xff0c;阻止潜在的攻击流量进入网络。它可以实现基于端口、协议、IP地址和应用程序的…

chatgpt赋能python:Python指定日期的后一天:介绍与解析

Python指定日期的后一天&#xff1a;介绍与解析 Python是广泛使用的编程语言之一&#xff0c;在数据科学、机器学习、人工智能和网络开发等众多领域都有重要的应用。本文将要讨论的问题是&#xff1a;如何使用Python来获取一个指定日期的后一天&#xff1f;这个问题看似简单&a…

QT找不到python27.dll或python36.dll解决办法

缺少python27.dll 点开QT时出现上述问题&#xff0c;一种方法就是找python27.dll文件进行替换。 1.进入网站下载相关文件 windll.com/dll/python-… 2.之后将文件移动到下面的路径里&#xff0c;C:\Windows\SysWOW64\ 3.然后重启 试了4种方案&#xff0c;把32位放在System32…

【操作系统】哲学界进餐问题实现

目录 实验原理&#xff1a; 实验目的&#xff1a; 实验数据及结果分析&#xff1a; 实验原理&#xff1a; 哲学家进餐问题可以通过限制同时进餐人数为4解决&#xff0c;为此需要设置一个值为4的互斥信号量sem_eaters,并且每根筷子只能同时由一个人使用&#xff0c;需要设置…

kafka的初步认识

什么是Kafka&#xff1f; Kafka的增长很快。财富500强企业中超过三分之一使用卡夫卡。这些公司包括十大旅游公司&#xff0c;十大银行中的七家&#xff0c;十大保险公司中的八家&#xff0c;十大电信公司中的九家&#xff0c;等等。LinkedIn&#xff0c;微软&#xff08;Micro…