C++语法|可调用对象与function类型

文章目录

  • 引入
  • function的使用
  • function类型的典型应用
  • function类型的原理
    • 实现
    • 代码优化
      • 可变参的函数对象

引入

还记得C语言中的函数指针数组吗?
我们通过函数指针数组实现一个,图书管理系统的界面:

#include <stdio.h>
void doShowAllBooks() {
    printf("查看所有书籍信息\n");
}
void doBorrow() {
    printf("借书\n");
}
void doBack() {
    printf("还书\n");
}
void doQueryBooks() {
    printf("查询书籍\n");
}
void doLoginOut() {
    printf("注销\n");
}
int main() {
    int choice = 0;
    void (*actionArray[5])() = {doShowAllBooks, doBorrow, doBack, doQueryBooks, doLoginOut};

    while (1) {
        printf("----------------\n");
        printf("1.查看所有书籍信息\n");
        printf("2.借书\n");
        printf("3.还书\n");
        printf("4.查询书籍\n");
        printf("5.注销\n");
        printf("----------------\n");
        printf("请选择: ");
        scanf("%d", &choice);

        if (choice < 1 || choice > 5) {
            printf("输入数字无效,重新选择\n");
        } else {
            // 调用相应的函数
            (*actionArray[choice - 1])();
        }
    }
    return 0;
}

我们通过函数指针数组集中保留了我们的函数,让巧用显得十分方便,那么在C++中,我们也有类似的实现。

function的使用

我觉得function简单一点来理解就是它能够保留可调用对象
function是一个模板,当我们创建这个模板的时候需要一些额外的信息,所谓的额外信息就是指该function类型能够表示的对象的调用形式:

function<int(int, int)>

表示我们生命里一个function类型,它可以接受两个int、返回一个int的可调用对象。

void hello1() {
    cout << "hello world!" << endl;
}

void hello2(string str) {    //void (*pfunc) (string)用函数指针调用
    cout << str << endl;
}

int sum(int a, int b) {
    return a + b;
}

class Test {
public: //成员方法的调用必须依赖一个对象void (Test::*pfunc) (string)
    void hello(string str) {cout << str << endl; }
};

int main () {
    //从function 的类模板定义处,用一个函数类型实例化function
    function<void()> func1 = hello1; // func1(hello1)
    func1(); //func1.operator() ==> hello1()

    function<void(string)> func2 = hello2;
    func2("hello hello2"); //dunc2.operator(string str) ==> hello2(str)

    function<int(int, int)> func3 = sum;
    cout << func3(1, 1) << endl;

    //保留函数对象 operator()
    function<int(int, int)> func4 = [](int a, int b)->int {return a + b; };
    cout << func4(100, 200) << endl;

    //对类的成员方法也可以进行保留  hello有两个参数 一个是this指针一个是string str
    function<void(Test*, string str)> func5 = &Test::hello;
    Test t;
    //func5(&Test(), "call Test::hello"); 报错,至少clang g++不允许
    func5(&t, "Call Test::hello");

    return 0;
}

function类型的典型应用

通过定义map和function类型实现一个读书管理系统。

void doShowAllBooks() { cout << "查看所有书籍信息" << endl; }
void doBorrow() { cout << "借书" << endl; }
void doBack() { cout << "还书" << endl; }
void doQueryBooks() { cout << "查询书籍" << endl; }
void doLoginOut() { cout << "注销" << endl; }

int main()
{
    int choice = 0;

    // 用C语言的函数指针其实也可以实现这个例子(采用数组来映射函数指针)
    // 但是他不能实现lambda表达式和函数对象
    map<int, function<void()>> actionMap;
    actionMap.insert({1, doShowAllBooks}); // insert(make_pair(xx, xx));
    actionMap.insert({2, doBorrow});
    actionMap.insert({3, doBack});
    actionMap.insert({4, doQueryBooks});
    actionMap.insert({5, doLoginOut});

    for (;;)
    {
        cout << "----------------" << endl;
        cout << "1.查看所有书籍信息" << endl;
        cout << "2.借书" << endl;
        cout << "3.还书" << endl;
        cout << "4.查询书籍" << endl;
        cout << "5.注销" << endl;
        cout << "----------------" << endl;
        cout << "请选择: ";
        cin >> choice;

        auto it = actionMap.find(choice);
        if (it == actionMap.end()) {
            cout << "输入数字无效,重新选择" << endl;
        } else {
            it->second();
        }

function类型的原理

通过举例来说明,有如下例子,func1可以打印hello world!

void hello(string str) { cout << str << endl; }
int main () {
	function<void(string str)> func1(hello); // func1 = hello;
	func1("hello world");
	return 0
}

实现

通过分析我们可以知道function我们可以使用模板类来实现它,在这个模板类中,我们需要使用一个函数指针来指向hello函数。然后需要重载函数调用运算符。

  • 首先我们需要实现一个通用的模板类my_function,这个通用模板类不需要任何实现。仅仅让编译器找到匹配的模板类定义
//my_function<void(string)> func1(hello);
template<typename Fty>
class my_function { };
  • 实现特例化的模板类
template<typename R, typename A1>
//my_function<void(string)> func1(hello);
class my_function<R(A1)> {
public:
	using PFUNC = R (*) (A1);
	my_function(PFUNC pfunc) : _func(pfunc) {}
	R operator() (A1 arg) const {return _pfunc(arg); } //hello(arg)
private:
	PFUNC _pfunc;
}
  • 调用检验
int main() {
    my_function<void(string)> func1(hello); // 定义了一个函数对象,类型为void(string)
    func1("Hello, world!"); // func1.operator()("Hello, world!");
    return 0;
}

代码优化

如果我们要使用function来保存int sum(int a, int b)呢?我们岂不是又要实现一个特例化的模板类?答案肯定是不用!

void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }
int main () {
	function<void(string)> func1(hello); // func1 = hello;
	func1("hello world");
	function<void(int a, int b)> func2(sum);
	cout << func2(10, 20) << endl;
	return 0
}

可变参的函数对象

template<typename R, typename... Args> //可变参的类型参数
class my_function<R(Args...)> {
public:
    using PFUNC =  R (*) (Args...);

    my_function(PFUNC pfunc) : _pfunc(pfunc) {}

    R operator()(Args... args) const { 
        return _pfunc(args...); 
    } //hello(arg)
private:
    PFUNC _pfunc;
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

口感与风味的完善结合:精酿啤酒的多样风格

啤酒的世界是丰富多彩的&#xff0c;不同的啤酒有着各自与众不同的口感和风味。而Fendi club啤酒&#xff0c;作为精酿啤酒的代表&#xff0c;以其多样化的风格和卓着的口感&#xff0c;吸引了无数啤酒爱好者的目光。 Fendi club啤酒的多样风格&#xff0c;首先体现在其原料的选…

医药垃圾分类管理系统|基于SSM医药垃圾分类管理系统的系统设计与实现(源码+数据库+文档)

医药垃圾分类管理系统 目录 基于SSM医药垃圾分类管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统登录模块 2管理员模块实现 3用户模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博…

1天搞定SpringBoot+Vue全栈开发 (8)前端路由VueRouter(进行组件切换)

1.VueRouter安装与使用 2.参数传递 创建路由组件 在项目中定义Discover.vue、Friends.vue、My.vue三个组件&#xff0c;将来要使用vue-router来控制它们的展示与切换&#xff1a; Discover.vue <template><div><h1>发现音乐</h1></div> <…

PE文件(四)FileBuffer-ImageBuffer

文件执行的总过程 当文件从硬盘中读入虚拟内存&#xff08;FileBuffer&#xff09;中时&#xff0c;文件数据会被原封不动的复制一份到虚拟内存中&#xff0c;然后进行拉伸对齐。此时虚拟内存中文件数据叫做文件印象或者内存印象&#xff0c;即ImageBuffer。此时ImageBuffer中…

Prompt提示词教程 | 提示工程指南 | 提示词示例 入门篇

在上一节中&#xff0c;我们介绍并给出了如何赋能大语言模型的基本示例。如果还没看而且是刚入门的同学建议看下&#xff0c;有个基本概念。 Prompt提示词教程 | 提示工程指南 | 提示工程简介https://blog.csdn.net/HRG520JN/article/details/138523705在本节中&#xff0c;我…

crossover怎么打开软件 mac怎么下载steam crossover下载的软件怎么运行

CrossOver是一款Mac和Linux平台上的类虚拟机软件&#xff0c;通过CrossOver可以运行Windows的可执行文件。如果你是Mac用户且需要使用CrossOver&#xff0c;但是不知道CrossOver怎么打开软件&#xff0c;如果你想在Mac电脑上玩Windows游戏&#xff0c;但不知道怎么下载Steam&am…

free5gc+ueransim配置

ueransim已完成配置&#xff0c;可以观察到在make后&#xff0c;ueransim的build下生成以下几个文件 nr-gnb-------------5G gnb&#xff08;RAN&#xff09;的主要可执行文件 nr ue---------------5G ue的主要可执行文件 nr-cli---------------5G gNB和UE的cli工具 nr-binder-…

从零开始的软件测试学习之旅(六)测试网络基础知识

测试网络基础知识 HTTP和HTMLURLDNS客户端和服务器请求方法和状态码面试高频Fiddler抓包工具教学弱网 HTTP和HTML 概念 html: HyperText Markup Language 超文本标记语言 http: HyperText Transfer Protocol 超文本传输协议 超文本: 图片, 音频, 视频 关系:http 可以对 html 的…

Cargo - 构建 rust项目、管理依赖包

文章目录 关于 Cargo构建项目创建工程编译运行buildclean 管理依赖添加依赖updatecheck计时 manual rust 安装可参考&#xff1a;https://blog.csdn.net/lovechris00/article/details/124808034 关于 Cargo Cargo 官方文档 &#xff1a; https://doc.rust-lang.org/cargo/crat…

QSPI的使用

Quad SPI接口(QSPI)是一种同步串行数据链路,在主模式下提供与外部设备的通信。它类似于SPI协议,只是它有额外的数据线。 普通SPI有四条通信线路:芯片选择、时钟、MOSI和MISO。对于QSPI,可提供额外的数据线。因此,命令/数据/地址是根据所选模式通过单、四或双IO发送的。由…

网络安全之动态路由OSPF基础

OSPF&#xff1a;开放式最短路径优先协议。 1、协议使用范围&#xff1a;IGP。 2、协议算法特点&#xff1a;链路状态型路由协议。 3、协议是否传递网络掩码&#xff1a;传递网络掩码&#xff08;无类别的路由协议&#xff09;。 4、协议封装&#xff1a;基于IP协议封装&am…

【Linux系统编程】1-文件IO操作

文章目录 1 概述2 文件描述符3 文件I/O操作3.1 打开文件操作3.2 关闭文件操作3.3 向文件写入数据3.4 从文件读取数据 4 给文件描述符添加非阻塞特性4.1 当此文件描述符不存在4.2 当此文件描述符存在 5 获取文件状态信息6 文件目录操作6.1 打开目录操作6.2 读取目录信息6.3 关闭…

窃鈇逃债,赧然惭愧——“天下共主”周赧王的结局

引子&#xff0c;债台高筑 周赧王五十九年&#xff08;前256年&#xff09;&#xff0c;雒邑王都内&#xff0c;大周第三十七代天子、年近八十的周赧王姬延困坐在王宫内的高台上&#xff0c;愁容满面、沮丧悲切、束手无策&#xff1b;而王宫宫墙外不远处&#xff0c;是一大帮举…

VMware与CentOS的安装

VMware与CentOS的安装 第一章 VMware安装第二章 CentOS上网虚拟机网络IP修改地址配置修改主机名和hosts文件修改主机名称配置Linux克隆机主机名称映射hosts文件&#xff0c;打开/etc/hosts 安装Xshell7和Xftp7 第一章 VMware安装 VMware Workstation Pro 安装包 …

【再探】设计模式—适配器、装饰及外观模式

结构型设计模式是用于设计对象和类之间关系的一组设计模式。一共有7种&#xff1a;适配器模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式及代理模式。 1 适配器模式 需求&#xff1a;在软件维护阶段&#xff0c;已存在的方法与目标接口不匹配&#xff0c;需要个中…

Windows系统和unbtun系统连接usb 3.0海康可见MVS和红外艾睿相机

一.海康可见USB3.0工业面阵相机 海康usb相机需要去海康官网上下载对应系统的MVS客户端及SDK开发包 海康机器人-机器视觉-下载中心 选择Windows系统和unbtun&#xff08;我是linux aarch64,所以选择了对应压缩包解压&#xff09; Windows系统 1.双击安装包进入安装界面&…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Date/Time Edit的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Spin Box的使用及说明 文章编号&#xff1…

【牛客】[HNOI2003]激光炸弹

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二维前缀和板题。 注意从&#xff08;1,1&#xff09;开始存即可&#xff0c;所以每次输入x,y之后&#xff0c;要x,y。 因为m的范围最大为…

nginx--FastCGI

CGI 概念 nginx通过与第三方基于协议实现&#xff0c;即通过某种特定协议将客户端请求转发给第三方服务处理&#xff0c;第三方服务器会新建新的进程处理用户的请求&#xff0c;处理完成后返回数据给Nginx并回收进程(下次处理有需要新建)&#xff0c;最后nginx在返回给客户端…

5.7 线程

进程&#xff1a;解耦稳定&#xff0c;内容之间是不相关的&#xff0c;通信不便利&#xff0c;理论上进程的软硬件的切换时间以及创建开销非常大。--------》资源共享线程实现 线程的问题&#xff1a;本质就是不解耦&#xff0c;一个出问题别的就很有可能出问题&#xff0c;同…
最新文章