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

文章目录

  • 可调用对象
    • 普通函数
    • 类的静态成员函数
    • 仿函数
    • 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");//定义类的成员函数指针Cvoid (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;//包装普通全局函数showfn1(1, "function包装器对全局函数");//类内有静态成员函数function<void(int, const string&)>fn2 = AA::show;//包装普通全局函数showfn2(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.hqwc.cn/news/472477.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

集群聊天项目

不懂的一些东西 (const TcpConnectionPtr&&#xff09;作为形参啥意思&#xff1a;接收一个常量引用&#xff0c;函数内部不允许修改该指针所指向的对象。 优势 1.网络层与业务层分离&#xff1a;通过网络层传来的id&#xff0c;设计一个map存储id以及对印的业务处理器&…

文生图提示词:天气条件

天气和气候 --天气条件 Weather Conditions 涵盖了从基本的天气类型到复杂的气象现象&#xff0c;为描述不同的天气和气候条件提供了丰富的词汇。 Sunny 晴朗 Cloudy 多云 Overcast 阴天 Partly Cloudy 局部多云 Clear 清晰 Foggy 雾 Misty 薄雾 Hazy 朦胧 Rainy 下雨 Showers …

Web前端-入门-MDN文档学习笔记

Web 入门 查看更多学习笔记&#xff1a;GitHub&#xff1a;LoveEmiliaForever MDN中文官网 安装基础软件 计算机&#xff1a;Windows、MacOS、Linux文本编辑器&#xff1a;VS Code&#xff08;推荐&#xff09;、Sublime Text、Atom、Vim等等IDE&#xff08;和文本编辑器二选…

【打工日常】使用docker部署可视化工具docker-ui

一、docker-ui介绍 docker-ui是一个易用且轻量化的Docker管理工具&#xff0c;透过Web界面的操作&#xff0c;方便快捷操作docker容器化工作。 docker-ui拥有易操作化化界面&#xff0c;不须记忆docker指令&#xff0c;仅需下载镜像即可立刻加入完成部署。基于docker的特性&…

DS:八大排序之直接插入排序、希尔排序和选择排序

创作不易&#xff0c;感谢三连支持&#xff01;&#xff01; 一、排序的概念及运用 1.1 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起 来的操作。稳定性&…

鸿蒙开发-HarmonyOS UI架构

初步布局Index 当我们新建一个工程之后&#xff0c;首先会进入Index页。我们先简单的做一个文章列表的显示 class Article {title?: stringdesc?: stringlink?: string }Entry Component struct Index {State articles: Article[] []build() {Row() {Scroll() {Column() …

Leecode之合并两个有序链表

一.题目及剖析 https://leetcode.cn/problems/merge-two-sorted-lists/description/ 二.思路引入 用指针遍历两个链表并实时比较,较小的元素进行尾插,然后较小元素的指针接着向后遍历 三.代码引入 /*** Definition for singly-linked list.* struct ListNode {* int va…

文心一言API使用教程(python版)

注&#xff1a;在做项目的时候需要调用文心一言&#xff0c;发现网上的版本很乱&#xff0c;基本都止步在官方文档的代码上&#xff0c;所以写了一篇博客来记录自己的尝试实现了对文心一言的循环调用和自定义询问&#xff0c;本篇文章不需要有任何对api的基础知识&#xff0c;代…

VitePress-17- 配置- appearance 的作用详解

作用说明 appearance : 是进行主题模式的配置开关&#xff0c;决定了是否启用深色模式。 可选的配置值&#xff1a; true: 默认配置&#xff0c;可以切换为深色模式&#xff1b; false: 禁用主题切换&#xff0c;只使用默认的配置&#xff1b; dark: 默认使用深色模式&#xff…

[01] Vue2学习准备

目录 vue理解创建实例插值表达式 {{}}响应式特性 vue理解 Vue.js 是一套构建用户界面的渐进式框架。 Vue 只关注视图层&#xff0c; 采用自底向上增量开发的设计。 Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 创建实例 准备容器 <div id…

Crypto-RSA3

题目&#xff1a;&#xff08;BUUCTF在线评测 (buuoj.cn)&#xff09; 共模攻击 ​ 前提&#xff1a;有两组及以上的RSA加密过程&#xff0c;而且其中两次的m和n都是相同的&#xff0c;那么就可以在不计算出d而直接计算出m的值。 ​ 设模数为n&#xff0c;两个用户的公钥分别为…

机器学习——聚类问题

&#x1f4d5;参考&#xff1a;西瓜书ysu老师课件博客&#xff08;3&#xff09;聚类算法之DBSCAN算法 - 知乎 (zhihu.com) 目录 1.聚类任务 2.聚类算法的实现 2.1 划分式聚类方法 2.1.1 k均值算法 k均值算法基本原理&#xff1a; k均值算法算法流程&#xff1a; 2.2 基于…