c++ 可调用对象的绑定器和包装器

news2025/3/12 19:31:40

文章目录

  • 可调用对象
    • 普通函数
    • 类的静态成员函数
    • 仿函数
    • lambda函数
    • 类的非静态成员函数 最重要的
    • 可被转换为函数指针的类对象
  • 包装器 function
  • 适配器bind
  • 可变函数和参数实现
  • 回调函数实现
  • 替代虚函数

可调用对象

在C++中,可以像函数一样调用的有:

普通函数、类的静态成员函数、仿函数、lambda 函数、类的成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象

可调用对象有类型,可以用指针存储他们的地址,可以被引用

普通函数

普通函数类型可以声明函数、定义函数指针和函数引用,但是,不能定义函数的实体

#include<iostream>
#include<string>
using namespace std;

using Fun = void(int,const string&);
Fun show;//声明普通函数show  不能用函数类型定义函数

void show(int x,const string&s){
	cout<< x <<"--"<< s <<endl;
}
int main(){
	show(1,"我是一个鸟");
	
	void (*f1)(int,const string&) = show;//函数指针指向函数
	void (&f2)(int,const string&) = show;//函数引用指向函数
	f1(2,"hello");
	f2(3,"world");

	Fun *f3 = show;//函数指针指向函数
	Fun &f4 = show;//函数引用指向函数
	f3(4,"hello");
	f4(5,"world");
    return 0;
}

类的静态成员函数

类的静态成员函数和普通函数本质上是一样的,把普通函数放在类中而已

#include<iostream>
#include<string>
using namespace std;

using Fun = void(int,const string&);
Fun show;//声明普通函数show  不能用函数类型定义函数
struct AA{
	static void show(int x,const string&s){
		cout<< x <<"--"<< s <<endl;
	}
};

int main(){
	AA::show(1,"我是一个鸟");
	
	void (*f1)(int,const string&) = AA::show;//函数指针指向函数
	void (&f2)(int,const string&) = AA::show;//函数引用指向函数
	f1(2,"hello");
	f2(3,"world");

	Fun *f3 = AA::show;//函数指针指向函数
	Fun &f4 = AA::show;//函数引用指向函数
	f3(4,"hello");
	f4(5,"world");
    return 0;
}

仿函数

本质是类

class TT{
public:
    void operator()(int b,const string&s){
        cout<< b<<"=="<<s <<endl;
    }
};


int main() {
    TT t;
    t(0,"dad");//对象调用
    TT()(1,"dasd");//匿名对象调用仿函数

    TT&t0 = t;
    t0(2,"dy");//对象的引用调用
	return 0;
}

lambda函数

本质是仿函数

auto fd = [](int x,const string&s){
    cout<< x<<"=="<<s<<endl;
};
int main() {
    fd(1,"du");

    auto &ff = fd;
    ff(2,"yxx");
}

类的非静态成员函数 最重要的

只有指针类型,没有引用类型

class CC{
public:
    void show(int b,const string&s){
        cout<< b<<"=="<<s <<endl;
    }
};
int main() {
    CC c;
    c.show(1,"dxx");

    //定义类的成员函数指针C
    void (CC::*f1)(int,const string&) = &CC::show;
    (c.*f1)(2,"nihao");

    //定义类的成员函数指针C++
    using Fun = void(CC::*)(int,const string&);
    Fun f2 = &CC::show;
    (c.*f2)(3,"xxxxx");
}

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

类可以重载类型转换运算符 operator 数据类型() 如果数据类型是函数指针或函数引用,那么该类的实例也将成为可调用对象

本质是类 调用的代码像函数
实际开发意义不大

void show(int x){
    cout<< x <<endl;
}
class CC{
public:
    using F = void(*)(int);
    operator F(){
        return show;//返回普通函数
        //只能返回普通全局函数和类的静态成员函数
    }
};
int main() {
    CC c;
    c(10);
}

包装器 function

包含头文件 functional

#include<iostream>
#include<functional>
#include<string>
using namespace std;

//普通函数
void show(int x, const string& s) {
	cout << "你好" << x << "==" << s << endl;
}

//类内有静态成员函数
struct AA{
	static void show(int x, const string& s) {
		cout << "你好" << x << "==" << s << endl;
	}
};

//仿函数
struct BB{
	void operator()(int x, const string& s) {
		cout << "你好" << x << "==" << s << endl;
	}
};

//lambda函数
auto f = [](int x, const string& s) {
	cout << "你好" << x << "==" << s << endl;
	};

//类内有普通成员函数
struct CC{
	void show(int x, const string& s) {
		cout << "你好" << x << "==" << s << endl;
	}
};

//可被转换为函数指针的类
struct DD {
	using Fun = void(*)(int, const string&);
	operator Fun() {
		return show;
	}
};

int main() {
	//普通函数
	//<函数返回类型(参数列表)>
	function<void(int, const string&)>fn1 = show;//包装普通全局函数show
	fn1(1, "function包装器对全局函数");

	//类内有静态成员函数
	function<void(int, const string&)>fn2 = AA::show;//包装普通全局函数show
	fn2(2, "function包装器对类内静态成员函数");

	//仿函数
	function<void(int, const string&)> fn3= BB();
	fn3(3, "function包装器对仿函数");

	//lambda函数
	function<void(int, const string&)> fn4 = f;
	fn4(4, "function包装器对lambda");

	//类内普通函数
	CC c;
	function<void(CC&c,int, const string&)>fn5 = &CC::show;
	fn5(c, 5, "function包装器对类内普通函数");

	//可被转换为函数指针的类
	DD d;
	function<void(int, const string&)>fn6 = d;
	fn6(6, "function包装器对可被转换为函数指针的类");
	return 0;
}

注意:

  • 重载了bool运算符,用于判断是否包装了可调用对象
  • 如果std::function对象未包装可调用对象,使用std::function对象将抛出std::bad_function_call异常

适配器bind

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

在这里插入图片描述

#include<iostream>
#include<functional>
#include<string>

using namespace std;

void show(int x, const string &s) {
    cout << x << "--" << s << endl;
}

struct AA {
    void show(int x) {
        cout << x << endl;
    }
};

int main() {
    function<void(int, const string &)> f1 = show;
    f1(1, "按照顺序");
    // placeholders::_n <>中的第n个和其所在位置的绑定
    // 传参的顺序按照 <> 的顺序
    function<void(const string &, int)> f2 = bind(show, placeholders::_2, placeholders::_1);
    f2("不按照顺序", 2);

    int x{5};
    // 绑定的时候默认是x值传递,要是引用要使用ref(x)
    function<void(const string &)> f3 = bind(show, ref(x), placeholders::_1);
    x = 500;
    f3("缺省");

    function<void(int, const string &, int)> f4 = bind(show, placeholders::_1, placeholders::_2);
    f4(4, "多参数", 111);


    //类的非静态成员函数
    AA a;
    function<void(int)> f5 = bind(&AA::show, &a, placeholders::_1);
    f5(45);

    return 0;
}

可变函数和参数实现

#include<iostream>
#include<functional>

using namespace std;

void print() {}

template<typename T, typename ...Args>
void print(T arg, Args...args) {
    cout << arg << endl;
    print(args...);
}

template<typename ...Args>
void biaobai(Args...arg) {
    print(arg...);
}
//万能表白函数
/**
 * @tparam Fn 可调用函数
 * @tparam Args 可变参数
 * @return bind(fn, args...)
 *
 * 如果传递的值是右值 移动赋值,&&和forward完美转发
 */
template<typename Fn, typename ...Args>
auto show_love(Fn fn, Args...args)-> decltype(bind(fn, args...)) {
    auto f = bind(fn, args...);
    f();
    return f;
}

void show0(const string &s) {
    cout << "亲爱的" << s << ",爱你" << endl;
}
void show1(){
    cout << "亲爱的"<< ",爱你" << endl;
}
auto f = [](const string&s){
    cout << "亲爱的" << s << ",爱你" << endl;
};

struct AA{
    void operator()(const string &s) {
        cout << "亲爱的" << s << ",爱你" << endl;
    }
};


int main() {
    show_love(show0,"ying");
    show_love(show1);
    show_love(AA(),"ying");

    return 0;
}

回调函数实现

在消息队列和网络库的框架中,当接收到消息(报文)时,回调用户自定义的函数对象,把消息(报文)参数传给它,由它决定如何处理

#include<iostream>
#include<functional>

using namespace std;

template<typename CallBackType>
void performCallback(const string &s, const CallBackType &callback) {
    callback(s);// 调用回调函数并传递消息
}

class MyCallBack {
public:
    void myFunc(const string &message) {
        cout << "我的信息是:" << message << endl;
    }
};

int main() {
    MyCallBack m;

    auto boundFunc = bind(&MyCallBack::myFunc,&m,placeholders::_1);

    performCallback("你好啊",boundFunc);
    return 0;
}

替代虚函数

C++虚函数在执行过程中会跳转两次(先查找对象的函数表,再次通过该函数表中的地址找到真正的执行地址),这样的话,CPU会跳转两次,而普通函数只跳转一次

CPU 每跳转一次,预取指令要作废很多,所以效率会很低(百度)

为了管理的方便 (基类指针可指向派生类对象和自动析构派生类) 保留类之间的继承关系。

包装器和绑定器可以代替虚函数的功能并且无性能损失

#include<iostream>
#include<functional>

using namespace std;

struct Hero {
    /*virtual void show(){
        cout<< "英雄释放了技能" <<endl;
    }*/
    function<void()> m_callback;  // 用于绑定子类的成员函数

    //注册子类成员函数,子类成员函数没有参数
    template<class Fn, class ...Args>
    void callback(Fn &&fn, Args &&...args) {
        m_callback = bind(forward<Fn>(fn), forward<Args>(args)...);
    }

    //调用子类的成员函数
    void show() {
        m_callback();
    }
};

struct XS : public Hero {
    void show() {
        cout << "西施释放了技能" << endl;
    }
};

struct HX : public Hero {
    void show() {
        cout << "韩信释放了技能" << endl;
    }
};

int main() {
    int id;
    cout << "请输入英雄id:";
    cin >> id;

    Hero *p = nullptr;
    if (id == 1) {
        p = new XS;
        p->callback(&XS::show, static_cast<XS *>(p));
    } else if (id == 2) {
        p = new HX;
        p->callback(&HX::show, static_cast<HX *>(p));
    }
    if (p != nullptr) {
        p->show();
        delete p;
    }

    return 0;
}

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

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

相关文章

解决IDEA的Project无法正常显示的问题

一、问题描述 打开IDEA&#xff0c;结果发现项目结构显示有问题&#xff1a; 二、解决办法 File -> Project Structure… -> Project Settings (选Modules)&#xff0c;然后导入Module 结果&#xff1a; 补充&#xff1a; IDEA提示“The imported module settings a…

深度学习系列58:大模型训练和压缩

1. 大模型训练 1.1数据并行 1.2 模型并行 1.3 ZeRO 1.4 流水线并行 1.5 混合精度训练 1.6 offloading 把梯度放在cpu上保存和计算 1.7 overlapping 提前传输数据 1.8 checkpointing 中间线性层不保存&#xff0c;反向传播时再次重新计算 1.9 使用BMtrain 2. 大模型压…

AutoSAR(基础入门篇)10.3-BswM配置

目录 一、ECU State Handing(ESH) 二、Module Initialization 三、Communication Control 说起BswM的配置,其实博主问过很多朋友了,大家基本都只用自动配置;很少有用到手动配置的时候,对于刚刚入门的大家来说,掌握自动配置基 本也就足够了。 一、ECU State Handing(ESH…

Vue首屏优化,12个提速建议

文章目录 代码拆分和懒加载&#xff1a;代码拆分懒加载 图片优化&#xff1a;组件懒渲染&#xff1a;数据预获取和缓存&#xff1a;服务器端渲染&#xff08;SSR&#xff09;&#xff1a;代码压缩和合并&#xff1a;使用 CDN 加速&#xff1a;监控和性能分析&#xff1a;代码优…

LeetCode Python - 22.括号生成

目录 题目答案运行结果 题目 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”] 示例…

中国半导体设备行业概述

TrendForce根据各种行业数据和主要代表性公司的最新财务报告综合分析指出&#xff0c;中国本土设备产业在半导体制造流程&#xff08;除光刻机外&#xff09;的各个阶段均有覆盖能力。具体来说&#xff0c;国产设备在中国本地市场中&#xff0c;在剥离、清洗及蚀刻等工艺上的本…

C++入门学习(三十)一维数组的三种定义方式

数组是什么&#xff1f; 数组&#xff08;Array&#xff09;是有序的元素序列。 若将有限个类型相同的变量的集合命名&#xff0c;那么这个名称为数组名。组成数组的各个变量称为数组的分量&#xff0c;也称为数组的元素&#xff0c;有时也称为下标变量。用于区分数组的各个元素…

数据库数据加密的 4 种常见思路的对比

应用层加解密方案数据库前置处理方案磁盘存取环节&#xff1a;透明数据加密DB 后置处理 最近由于工作需要&#xff0c;我对欧洲的通用数据保护条例做了调研和学习&#xff0c;其中有非常重要的一点&#xff0c;也是常识性的一条&#xff0c;就是需要对用户的个人隐私数据做好加…

操作系统原理与实验——实验一队列

实验指南 运行环境&#xff1a; Dev c 算法思想&#xff1a; 队列顺序存储用链表来表示&#xff0c;用front指针表示队首&#xff0c;用rear指针表示队尾&#xff0c;实现队列的几个基本的操作。 核心数据结构&#xff1a; typedef int datatype; typedef struct { datatype a…

计算机网络——15套接字编程

套接字编程 Socket编程 Socket编程&#xff1a;应用进程使用传输层提供的服务才能够交换报文&#xff0c;实现应用协议&#xff0c;实现应用 TCP/IP&#xff1a;应用进程使用Socket API访问传输服务 地点&#xff1a;界面上的SAP 方式&#xff1a;Socket API 目标&#xff1…

【精选】Java面向对象进阶——接口细节:成员特点和接口的各种关系

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

STM32学习笔记(七) —— DMA传输(MTM)

DMA&#xff0c;全称是Direct Memory Access&#xff08;直接内存访问&#xff09;。可以在存储器和存储器之间或者外设和存储器之间传输数据&#xff0c;而不需要CPU的干预&#xff0c;这样可以节省CPU的资源&#xff0c;提高工作效率。 1.功能框图 STM32F103RCT6有两个DMA控…

怎样让MCU/SFU视频会议ovmedia 接入GB28281监控视频参会互动

在国内视频应用对GB监控接入是常规操作&#xff0c;很多系统需要接入监控视频交互处理。我们以ovmedia视频会议为例做一个接入互动。 GB28181协议在流媒体系统较为普及&#xff0c;我们以开源SRS系统对接监控端再接入会议&#xff08;也可以用商用GB流平台&#xff0c;操作基本…

嵌入式——EEPROM(AT24C02)

目录 一、初识AT24C02 1. 介绍 2. 引脚功能 补&#xff1a; 二、AT24C02组成 1. 存储结构 2. AT24C02通讯地址 3. AT24C02寻址方式 &#xff08;1&#xff09;芯片寻址 &#xff08;2&#xff09;片内子地址寻址 三、AT24C02读写时序 1. 写操作 &#xff08;1&…

C语言——从头开始——深入理解指针(1)

一.内存和地址 我们知道计算上CPU&#xff08;中央处理器&#xff09;在处理数据的时候&#xff0c;是通过地址总线把需要的数据从内存中读取的&#xff0c;后通过数据总线把处理后的数据放回内存中。如下图所示&#xff1a; 计算机把内存划分为⼀个个的内存单元&#xff0c;每…

人机交互新研究:MIT开发了结合脑电和眼电的新式眼镜,与机器狗交互

还记得之前的AI读心术吗&#xff1f;最近&#xff0c;「心想事成」的能力再次进化&#xff0c; ——人类可以通过自己的想法直接控制机器人了&#xff01; 来自麻省理工的研究人员发表了Ddog项目&#xff0c;通过自己开发的脑机接口&#xff08;BCI&#xff09;设备&#xff…

16.Qt 工具栏生成

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 1. 界面添加 2. 信号槽 功能实现 参考&#xff1a; 前言&#xff1a; 基于QMainWindow&#xff0c;生成菜单下面的工具栏&#xff0c;可以当作菜单功能的快捷键&#xff0c;也可以完成新的功能 直接在UI文件中…

django中事务和锁

目录 一&#xff1a;事务&#xff08;Transactions&#xff09; 二&#xff1a;锁 在Django中&#xff0c;事务和锁是数据库操作中的两个重要概念&#xff0c;它们用于确保数据的完整性和一致性。下面我将分别解释这两个概念在Django中的应用。 一&#xff1a;事务&#xff…

值得收藏的的适用于 Windows 11 的免费数据恢复软件榜单

终于要说到Windows 11了&#xff0c;有太多令人惊叹的功能&#xff0c;让人跃跃欲试。但是&#xff0c;在升级到 Windows 11 或使用 Windows 11 时&#xff0c;人们可能会因计算机问题而导致文件被删除或丢失。这就是为什么需要 Windows 11 的免费文件恢复的原因。这是适用于 W…

vscode右键菜单栏功能说明

本文主要介绍在vscode中的python代码文件中&#xff0c;单击鼠标右键出现的菜单栏功能。部分功能可能与安装插件相关&#xff0c;主要用于个人查阅。 单击右键菜单栏如下&#xff1a; GO to xx类型命令 “Go to Definition”、“Go to Declaration”、"Go to Type Defin…