C++之可调用对象、bind绑定器和function包装器

news2024/11/16 3:30:58

可调用对象

在C++中,可以像函数一样调用的有:普通函数、类的静态成员函数、仿函数、lambda函数、类的非静态成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象。

可调用对象有类型,可以用指针存储它们的地址,可以被引用(类的成员函数除外)。

这里举几个例子

仿函数(本质是重载了()的类)

#include<iostream>
using namespace std;
struct Object
{
    void operator()(int age, string name)
    {
        cout << "年龄:" << age << ",姓名:" << name << endl;
    }
};
int main()
{
    Object obj;
    obj(20, "小谢");
    Object& obj_r = obj;    // 引用函数
    obj_r(19, "小赵");
    return 0;
}

lambda函数

#include<iostream>
using namespace std;
int main()
{
    auto func = [](int age, string name)
    {
        cout << "年龄:" << age << ",姓名:" << name << endl;
    };
    func(20, "小谢");
    auto& func_r = func;// 引用lambda对象。
    func_r(19, "小赵");
    return 0;
}

类的非静态成员函数

类的非静态成员函数有地址,但是,只能通过类的对象才能调用它,所以,C++对它做了特别处理。

类的非静态成员函数只有指针类型,没有引用类型,不能引用。

#include<iostream>
using namespace std;
struct Object
{
    void show(int age, string name)
    {
        cout << "年龄:" << age << ",姓名:" << name << endl;
    }
};
int main()
{
    Object obj;
    obj.show(20, "小谢");
    void(Object::*pobj)(int, string) = &Object::show;// 定义类的成员函数的指针。
    (obj.*pobj)(19, "小赵");
    using PFun = void(Object::*)(int, string);
    PFun p_show = &Object::show;
    (obj.*p_show)(18, "芜湖");
    return 0;
}

在上面的例子中满足条件的这些可调用对象对应的类型被统称为可调用类型。C++ 中的可调用类型虽然具有比较统一的操作形式,但定义方式五花八门,这样在我们试图使用统一的方式保存,或者传递一个可调用对象时会十分繁琐。现在,C++11通过提供std::function 和 std::bind统一了可调用对象的各种操作。

包装器function

包含头文件:#include <functional>

std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;

#include<iostream>
#include<functional>
using namespace std;
int add(int a, int b)
{
    cout << a << "+" << b << "=" << a + b << endl;
    return a + b;
}
class T
{
public:
    static int sub(int a, int b)
    {
        cout << a << "*" << b << "=" << a * b << endl;
        return a * b;
    }
};
class T1
{
public:
    int operator()(int a, int b)
    {
        cout << a << "-" << b << "=" << a - b << endl;
        return a - b;
    }
};
int main()
{
    //std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
    function<int(int, int)> f1 = add;
    f1(1, 2);
    function<int(int, int)> f2 = T::sub;
    f2(2, 3);
    T1 t;
    function<int(int, int)> f3 = t;
    f3(3, 4);
    return 0;
}

通过测试代码可以得到结论:std::function 可以将可调用对象进行包装,得到一个统一的格式,包装完成得到的对象相当于一个函数指针,和函数指针的使用方式相同,通过包装器对象就可以完成对包装的函数的调用了。

作为回调函数使用

#include <iostream>
#include <functional>
using namespace std;
class A
{
public:
    // 构造函数参数是一个包装器对象
    A(const function<void()>& f) : callback(f)
    {
    }

    void notify()
    {
        callback(); // 调用通过构造函数得到的函数指针
    }
private:
    function<void()> callback;  //成员变量->包装器对象
};

class B
{
public:
    void operator()()
    {
        cout << "!!!" << endl;
    }
};
int main(void)
{
    B b;
    A a(b);
    a.notify();
    return 0;
}

绑定器bind

std::bind()模板函数是一个通用的函数适配器(绑定器),它用一个可调用对象及其参数,生成一个新的可调用对象,以适应模板。

函数原型

template< class Fx, class... Args >
      function<> bind (Fx&& fx, Args&...args);
Fx:需要绑定的可调用对象
args:/*绑定参数列表,可以是左值、右值和参数占位符std::placeholders::_n,如果参数不是占位符,缺省为值传递,std:: ref(参数)则为引用传递。*/

std::bind()返回std::function的对象。

std::bind()的本质是仿函数。

// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);

类成员函数需要绑定该类的this指针 。

#include<iostream>
#include<functional>
using namespace std;
struct Object
{
    void operator()(int age, string name)
    {
        cout << "年龄:" << age << ",姓名:" << name << endl;
    }
   
    void show(int age, string name)
    {
        cout << "年龄:" << age << ",姓名:" << name << endl;
    }
 
};
int main()
{
    Object obj;
    function<void(int, string)> f1 = bind(Object(), placeholders::_1, placeholders::_2);
    f1(20, "小谢");

    auto func = [](int age, string name)
    {
        cout << "年龄:" << age << ",姓名:" << name << endl;
    };
    function<void(int, string)> f2 = bind(func, placeholders::_1, placeholders::_2);
    f2(19, "小赵");

    // 类成员函数需要绑定该类的this指针  
    Object obj1;
    function<void(Object&, int, string)> f3 
    = bind(&Object::show, placeholders::_1, placeholders::_2, placeholders::_3);
    f3(obj1,17,"张三");
    //这里为了统一,将对象提前绑定
    function<void(int, string)> f4 
     = bind(&Object::show, obj1, placeholders::_1, placeholders::_2);
    f4(16, "李四");
    return 0;
}

在用绑定器绑定类成员函数或者成员变量的时候需要将它们所属的实例对象一并传递到绑定器函数内部。

bind的应用

改变参数位置

例如函数需要一个int和string两个参数

auto f = bind(func, placeholders::_1, placeholders::_2);

第一个参数为int,第二个为string,但是如果第一个想第一个传入string,第二个传入int

auto f = bind(func, placeholders::_2, placeholders::_1);

改变参数个数

改变参数个数主要是为了统一,便于使用函数模板,例如上述例子的部分代码

Object obj1;
function<void(Object&, int, string)> f3 
= bind(&Object::show, placeholders::_1, placeholders::_2, placeholders::_3);
f3(obj1,17,"张三");
//这里为了统一,将对象提前绑定
function<void(int, string)> f4 
 = bind(&Object::show, obj1, placeholders::_1, placeholders::_2);
f4(16, "李四");

这里采取的是提前绑定,将对象提前绑定。

设置类成员函数为回调函数

在讲bind时上面已演示!!

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

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

相关文章

孙子兵法-36计

目录 04、攻其无备&#xff0c;出其不意。——《孙子兵法始计篇》 08、不战而屈人之兵&#xff0c;善之善者也。——《孙子兵法谋攻篇》 09、上兵伐谋&#xff0c;其次伐交&#xff0c;其次伐兵&#xff0c;其下攻城。 01、兵者&#xff0c;国之大事&#xff0c;死生之地&…

想要的古风女生头像让你快速get

如今我看到很多人都喜欢用古风女生当作头像&#xff0c;那么今天我就来教大家如何快速得到一张超美的古风女生头像~ 上图就是我使用 APISpace 的 AI作画(图像生成)服务 快速生成的古风女生头像&#xff0c;不仅可以限定颜色&#xff0c;还可以选择『宝石镶嵌』或『花卉造型』这…

【HAL库】STM32CubeMX开发----STM32F407----Uart串口接收空闲中断

一、Uart串口接收空闲中断----详解 首先介绍串口通信的数据传输方式&#xff0c;这样后面的Uart串口空闲中断能更好的理解。 Uart串口通信----数据传输方式 串口通信的数据由发送设备通过自身的TXD接口传输到接收设备得RXD接口。 一个字符一个字符地传输&#xff0c;每个字符…

设计模式C++实现11:观察者模式

参考大话设计模式&#xff1b; 详细内容参见大话设计模式一书第十四章&#xff0c;该书使用C#实现&#xff0c;本实验通过C语言实现。 观察者模式又叫做发布-订阅&#xff08;Publish/Subscribe&#xff09;模式。 观察者模式定义了一种一对多的依赖关系&#xff0c;让多个观察…

Windows安装Gradle(IDEA兼容版)

IDEA兼容版本 IDEA安装目录下查看兼容Gradle版本&#xff1a;D:\win11\program\idea_2022.2.3\plugins\gradle\lib Gradle下载环境变量 1.创建仓库目录 D:\win11\program\gradle-7.4-bin\gradle-7.4\repository2.添加环境变量 GRADLE_HOME&#xff1a;D:\win11\program\gradle…

Java连接Redis

Jedis是Redis官方推荐的Java连接开发工具。api&#xff1a;https://tool.oschina.net/apidocs/apidoc?apijedis-2.1.0一、 导入包<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency><groupId>redis.clients</groupId><…

在职阿里6年,一个29岁女软件测试工程师的心声

简单的先说一下&#xff0c;坐标杭州&#xff0c;14届本科毕业&#xff0c;算上年前在阿里巴巴的面试&#xff0c;一共有面试了有6家公司&#xff08;因为不想请假&#xff0c;因此只是每个晚上去其他公司面试&#xff0c;所以面试的公司比较少&#xff09;其中成功的有4家&…

新版bing(集成ChatGPT)的申请方法

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,科大讯飞比赛第三名,CCF比赛第四名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

redis知识汇总(部署、高可用、集群)

文章目录一、redis知识汇总什么是redisredis的优缺点&#xff1a;为什么要用redis做缓存redis为什么这么快什么是持久化redis持久化机制是什么&#xff1f;各自优缺点&#xff1f;AOF和RDB怎么选择redis持久化数据和缓存怎么做扩容什么是事务redis事务的概念ACID概念主从复制re…

03 OpenCV图像运算

文章目录1 普通加法1 加号相加2 add函数2 加权相加3 按位运算1 按位与运算2 按位或运算、非运算4 掩膜1 普通加法 1 加号相加 在 OpenCV 中&#xff0c;图像加法可以使用加号运算符&#xff08;&#xff09;来实现。例如&#xff0c;如果要将两幅图像相加&#xff0c;可以使用…

JVM - 类加载,连接和初始化

目录 类加载和类加载器 概述 类加载要完成的功能 加载类的方式 类加载器 类加载器的关系 类加载器说明 双亲委派模型 工作过程如下&#xff1a; 双亲委派模型说明&#xff1a; 破坏双亲委派模型&#xff1a; 类连接和初始化 类连接主要验证的内容 类连接中的解析…

c++重中之重:“换个龟壳继续套娃“:运算符重载等的学习

文章目录 前言一.运算符重载二.const成员三.取地址重载总结前言 上一期我们讲到类的6个默认构造函数中的拷贝构造函数&#xff0c;这一期我们继续往下讲&#xff0c;当然难点肯定是运算符重载了。 一、运算符重载 运算符重载是c为了增强代码的可读性引入了运算符重载&#xf…

笑死,面试官又问我SpringBoot自动配置原理

面试官&#xff1a;好久没见&#xff0c;甚是想念。今天来聊聊SpringBoot的自动配置吧&#xff1f; 候选者&#xff1a;嗯&#xff0c;SpringBoot的自动配置我觉得是SpringBoot很重要的“特性”了。众所周知&#xff0c;SpringBoot有着“约定大于配置”的理念&#xff0c;这一…

亿级高并发电商项目-- 实战篇 --万达商城项目 四(Dashboard服务、设置统一返回格式与异常处理、Postman测试接口 )

专栏&#xff1a;高并发---前后端分布式项目 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、…

Spring-事务2

文章目录前言一、事务的特性&#xff08;ACID&#xff09;二、事务的隔离级别三、spring中的事务平台事务管理器.事务定义ISOLation_XXX&#xff1a;**事务隔离级别.**PROPAGATION_XXX&#xff1a;**事务的传播行为**.事务状态关系&#xff1a;四、使用XML文件配置事务1、 搭建…

2023前端面试题——JS篇

1.判断 js 类型的方式 1. typeof 可以判断出’string’,‘number’,‘boolean’,‘undefined’,‘symbol’ 但判断 typeof(null) 时值为 ‘object’; 判断数组和对象时值均为 ‘object’ 2. instanceof 原理是 构造函数的 prototype 属性是否出现在对象的原型链中的任何位置 …

Vue原理解析

文章目录1. VUE的响应式原理1.1 ViewModel1.2 双向绑定的基本原理1.3 什么是响应性1.4 Vue 中的响应性是如何工作的2. Vue 渲染机制2.1 虚拟 DOM2.2 渲染管线2.3 带编译时信息的虚拟 DOM2.3.1 静态提升2.3.2 修补标记 Flags2.3.3 树结构打平2.3.4 对 SSR 激活的影响1. VUE的响应…

Zookeeper安装和基本使用

目录标题一、下载二、安装三、启动客户端测试四、使用zk一、下载 注意&#xff1a;自zk3.5.5版本以后&#xff0c;已编译的jar包&#xff0c;尾部有bin&#xff0c;应该使用的是apache-zookeeper-3.8.0-bin.tar.gz。&#xff0c;因此在下载高版本时&#xff0c;因该下载后缀带b…

华为手表开发:WATCH 3 Pro(5)点击按钮弹窗

华为手表开发&#xff1a;WATCH 3 Pro&#xff08;5&#xff09;点击按钮弹窗初环境与设备创建项目认识目录结构修改首页 -> 新建按钮 “ 按钮 ”文件名&#xff1a;**index.hml**引用包&#xff1a;system.prompt点击结果初 鸿蒙可穿戴开发 希望能写一些简单的教程和案例…

三大指标继续狂飙!重庆啤酒:不惧强弱分化加剧,深耕高端市场

十多年前&#xff0c;重庆啤酒因为9个跌停而被一片唱衰&#xff0c;资本市场经典的“关灯吃面”典故自此出现&#xff0c;被股民沿用至今。不过自2020年&#xff0c;重庆啤酒开始逆转走势&#xff0c;股价连续上涨。2021年重庆啤酒营收突破百亿大关&#xff0c;净赚11.66亿元&a…