C++-第十七章:包装器

embedded/2025/3/1 17:23:12/

目录

第一节:std::function

第二节:std::bind

        2-1.基本介绍

         2-2.调整顺序(不常用)

        2-3.调整个数

        2-4.std::bind与std::function

下期预告:


        C++中有3种可调用对象:函数指针、仿函数对象、lambda函数,经过包装器包装后屏蔽了底层细节,就可以用更加通用的方式使用3种可调用函数了。

第一节:std::function

        std::function的底层实现其实是仿函数对象,通过嵌套一层对象,实现了3种可调用对象在类型上的统一(都变成了对象)。

        std::function包含在头文件<functional>中,它的使用方法如下:

std::function<返回类型(参数类型)> 名字 = 可调用对象;
#include <functional>// 包装函数指针
int Add(int a,int b)
{return a + b;
}
std::function<int(int, int)> f1 = Add;// 包装仿函数
class Func
{
public:int operator()(int a, int b){return a + b;}
};
std::function<int(int, int)> f2 = Func();// 包装lambda函数
std::function<int(int, int)> f3 = [](int a, int b) {return a + b; };int main()
{std::cout << f1(1, 2) << std::endl;std::cout << f2(1, 2) << std::endl;std::cout << f3(1, 2) << std::endl;return 0;
}

   

        如果封装的是类中的非静态成员函数,参数记得加上隐含的this指针类型,而且只有公有函数才能被封装:

class Person
{
public:void print(){std::cout << "我被调用了!" << std::endl;}
};
std::function<void(Person*)> f = &Person::print; // 这个&必不可少

        调用时也要传入一个对象的指针:

int main()
{Person LiHua;f(&LiHua); // 等价于 LiHua.print();return 0;
}

  

        若包装时使用Person作为参数,而不是指针,就可以直接传入对象进行调用:

#include <functional>
class Person
{
public:void print(){std::cout << "我被调用了!" << std::endl;}
};
std::function<void(Person)> f = &Person::print; // 这个&必不可少
int main()
{Person p;f(p);return 0;
}

  

        它们本质上都是使用对象调用成员函数。

        如果是类中的公有静态函数,则不需要隐含的this指针类型:

#include <functional>
class Person
{
public:static void print(){std::cout << "我被调用了!" << std::endl;}
};
std::function<void()> f = &Person::print; // 这个&可以省略
int main()
{f(); // 等价于 LiHua.print();return 0;
}

   

         

第二节:std::bind

        使用bind可以调整函数参数的传入顺序和个数。

        2-1.基本介绍

        std::bind的原型如下:

template<class Fn,class ...Args>
bind(Fn&& fn,Args&&... args);template<class Ret,class Fn,class ...Args>
bind(Fn&& fn,Args&&... args);

        模版参数包就包含了可调用对象的所有参数类型。 

        std::bind的使用格式如下:

std::bind(可调用对象,占位符1,占位符2,...);

        占位符有多个,而且占位符与参数是一一对应的,占位符1表示第一个参数,占位符2表示第二个参数。

        

         2-2.调整顺序(不常用)

        因为占位符1表示第一个参数,占位符2表示第二个参数,只要把占位符1放在后面,那么第一个参数就需要在后面传入了:

#include <functional>
void func(int a, float b, std::string c)
{std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl;
}
int main()
{auto f = std::bind(&func,std::placeholders::_3, std::placeholders::_2, std::placeholders::_1);return 0;
}

        像这样,它的占位符顺序是3、2、1,而不是正常的1、2、3。那么使用 f 时参数类型应该先传入std::string,再传入float,最后传入int:

	f("Hello", 0.2, 12);

  

        只是顺序改变了,对原函数的逻辑没有影响。

        2-3.调整个数

        调整个数不是真正的减少参数个数,实际上是指定某个或者某些参数的值,调用时这些参数就赋值为指定的值。

        指定后,剩下的参数再重新分占位符1、2、3:

#include <functional>
void func(int a, float b, std::string c)
{std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl;
}
int main()
{// 指定第一个参数的值为12auto f = std::bind(&func,12, std::placeholders::_1, std::placeholders::_2);// 第一个参数已经预先传好了,只传剩下两个参数f(0.2,"Hello");return 0;
}

   

        这种用法的主要场景是在类中进行函数传参时,如果一个函数类型的参数只接收3个参数的可调用对象,而对象中的非静态函数有3个显式的参数+1个this指针,并且这个函数因为需要用到对象的数据而不能设置为静态,也不能减少参数。这时候将 this 作为指定为 this指针 的值,它就变成只用传入3个参数的可调用对象了。

        例如:

        它的第一个参数就被指定为 this。

        2-4.std::bind与std::function

         可调用对象被std::bind包装过后也是可调用对象,所以可以将std::bind后的可调用对象传给std::function管理起来:

#include <functional>
void func(int a, float b, std::string c)
{std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl;
}
int main()
{// 如果指定了值,那么<>中的参数也省略被指定的类型 std::function<void(float,std::string)> f = std::bind(&func, 12, std::placeholders::_1, std::placeholders::_2);// 第一个参数已经预先传好了,只传剩下两个参数f(0.2,"Hello");return 0;
}

  

下期预告:

        下一章将讲述C++11的线程库,包括线程的管理、锁。


http://www.ppmy.cn/embedded/169103.html

相关文章

【AutoSar CP】SoAd模块

【AutoSar CP】SoAd模块 文章目录 【AutoSar CP】SoAd模块1. SoAd总体说明1.1. SoAd在AutoSar CP中的层级关系1.2. SoAd的作用2. 主要配置参数3. Sock连接4. PDU传输5. PDU接收6. 配置参数详表SoAdBswModulesSoAdGeneralSoAdConfigSoAdSocketConnectionGroupSoAdSocketConnecti…

【Qt】为程序增加闪退crash报告日志

背景 随着软件代码量的增加&#xff0c;软件崩溃闪退的肯能行越来越大&#xff0c;其中一些是难以复现的&#xff0c;比如访问了访问了非法地址、被操作系统杀死等。 为此&#xff0c;在软件出现闪退情况时&#xff0c;尽可能多的记录闪退发生时信息&#xff0c;对排查闪退原…

MySQL 和 PostgreSQL 的详细对比

以下是 MySQL 和 PostgreSQL 的详细对比&#xff0c;帮助您根据需求选择合适的数据库&#xff1a; 1. 核心特性对比 特性MySQLPostgreSQL数据库类型关系型数据库 (RDBMS)关系型数据库 (RDBMS)&#xff0c;支持部分 NoSQL 特性SQL 标准兼容性部分兼容&#xff08;简化语法&…

Linux 基本开发工具的使用(yum、vim、gcc、g++、gdb、make/makefile)

文章目录 Linux 软件包管理器 - yum理解什么是软件包和yum如何查看/查找软件包如何安装软件如何实现本地机器和云服务器之间的文件互传如何卸载软件 Linux 编辑器 - vim 的使用vim 的基本概念vim 的基本操作vim 命令模式各命令汇总vim 底行模式各命令汇总vim 的简单配置 Linux …

校园二手交易微信小程序的设计与实现(论文源码调试讲解)

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

【护网行动-红蓝攻防】第一章-红蓝对抗基础 认识红蓝紫

1.实战攻防演练 1.1为什么要进行实战攻防演练&#xff1f; 军事上的演练&#xff0c;是除了实战以外最能检验军队战斗力的一种考核方式&#xff0c;他可以模拟面对外部势力的攻击时候&#xff0c;如何更好的去维护国家和主权的安全。同样的&#xff0c;在网络上面&#xff0c;…

菜鸟之路Day18一一IO流综合练习

菜鸟之路Day18一一IO流综合练习 作者&#xff1a;blue 时间&#xff1a;2025.2.21 文章目录 菜鸟之路Day18一一IO流综合练习0.概述1.生成假数据&#xff08;网页爬虫&#xff09;2.随机点名器2.1随机点名器12.2随机点名器22.3随机点名器32.4随机点名器42.5随机点名器5&#x…

结构型模式---享元模式

概念 享元模式是一种结构型设计模式&#xff0c;他摒弃了在每个对象中保存所有数据的方式&#xff0c;通过共享多个对象所共有的相同状态&#xff0c;让你能在有限的内存容量中载入更多对象。享元模式将原始类中的数据分为内在状态数据和外在状态数据。 内在状态&#xff1a;就…