C++开发基础——函数对象与std::function模板

一,函数对象

1.函数对象的概念

函数对象可以像函数那样被直接调用。

函数对象(function objects)又被称为仿函数(functors)。

函数对象可以被当作一个值赋给另一个变量,也可以作为实参传递给其他函数,或者作为其他函数的返回结果。

函数对象与函数指针相似,函数对象的行为和函数差不多,但是与函数指针不同的是,函数对象是完整的类对象,里面包含着成员变量和多个成员函数。

函数对象的用法如下:

//class可以换成struct
class FunctionObjName{
public:ReturnType operator()(ParamType1, ... , ParamTypeN){process code}
};

2.函数对象的应用

函数对象的实现,本质上是在类中完成函数调用运算符的重载。因此,使用函数对象的重点在于重载"operator()"。

使用函数对象的步骤: 

step.01: 新建一个类,在类中定义一个重载的函数调用运算符

class Less
{
public:bool operator()(int a,int b) const;
};
//Less类的成员函数:函数调用运算符operator()

step.02: 定义函数运算符operator()的具体操作

bool Less::operator()(int a, int b) const
{return a < b;
}

step.03: 新建一个函数对象,并像调用函数一样调用函数对象

Less less_obj;
const bool_is_less = less_obj(5, 6);

3.标准库中的函数对象

STL标准库中提供了很多函数对象的类模板,它们都包含在头文件functional中。

例如上面提到的Less类,可以使用标准库中的"std::less<int>less"。从C++14标准开始,可以省略类型实参,例如"std::less<>less"。

标准库中常见的函数对象:

调用方式样例:

//方式一,直接调用
std::cout << std::plus<int>()(4, 5) << std::endl;
//方式二,实例化一个新的类,然后调用
std::plus<int> plus_obj;
std::cout << plus_obj(4, 5) << std::endl;

4.函数对象的传参

关于调用的时候传参,使用函数指针的开发场景更多时候是通过回调函数(超链接)来实现的,但是使用函数对象的开发场景有更加简洁的传参方式,它可以将用户传的参数放在对象的成员变量中。

代码样例:

#include <cmath>
class Nearer
{
public:Nearer(int value):{n=value;}bool operator()(int a,int b) const {return std::abs(a - n) < std::abs(b - n);};
private:int n;
};

5.C++代码样例

Demo_1: 

#include <iostream>
#include <vector>
#include <cmath> //For std::abs()
//用于对vector中逐个元素进行操作的模板函数
template <typename T, typename Process_type>
const T* find_optimum(const std::vector<T>& values, Process_type process)
{if (values.empty())return nullptr;const T* res = &values[0];for (size_t i = 1; i < values.size(); ++i){if (process(values[i], *res)){res = &values[i];}}return res;
}class Nearer
{
public:Nearer(int value): n(value){ }//重载函数调用运算符bool operator()(int a, int b) const {return std::abs(a - n) < std::abs(b - n);};
private:int n;
};class Less
{
public://重载函数调用运算符bool operator()(int a, int b) const {return a < b;}
};int main()
{Less less_obj;std::vector<int> numbers{ 23, 18, 17, 66, 40, 50 };std::cout << "Minimum element: " << *find_optimum(numbers, less_obj) <<  std::endl;std::cout << "The number nearest 36 is: " << *find_optimum(numbers, Nearer(50))  << std::endl;
}

运行结果: 

Minimum element: 17
The number nearest 36 is: 50

Demo_2:

#include <iostream>
#include <vector>
#include <algorithm>
class MeanValue {
private:int num;int sum;
public:MeanValue() : num(0), sum(0) {}//function callvoid operator()(int elem) {++num;sum += elem;}double get_mean_value() {return static_cast<double>(sum) / static_cast<double>(num);}
};
int main()
{std::vector<int> data_list = { 1, 2, 3, 4, 5, 6, 7, 8};MeanValue mv_obj = std::for_each(data_list.begin(), data_list.end(),  MeanValue());std::cout << "mean value: " << mv_obj.get_mean_value() << std::endl;
}

运行结果: 

mean value: 4.5

二,标准库中的std::function模板

1.std::function简介

std::function<>是C++11标准引入的类模板。

std::function<>专门用来包装可调用的函数对象。

在"<>"里面传入返回值类型和传参类型就可以开始使用std::function<>了。

std::function<>用法如下:

std::function<ReturnType(ParamType1, ... , ParamTypeN)>

std::function<>类模板的特点是,可以通过指定的类型参数,来统一处理设定返回值类型和参数类型

的各种函数对象。

std::function<int(int)> 可以用来专门调用返回值是int类型,形参是int类型的函数对象。

因此,有了std::function<>,不同实现的各种函数对象可以共用同一种调用形式(call signature)。

实例化以后的std::function<>,例如std::function<int(int)>,可以被理解为是某种特定调用形式的一个容器。

2.std::function具体用法

std::function<>被实例化以后可以调用:

普通函数

函数对象

lambda表达式。

用法演示:

应用场景:std::function<int(int, int)> 

 如下定义了返回值为int类型,传参为(int, int)的三种实现方式: 

add -->普通函数实现

mod -->lambda表达式实现

divide -->函数对象实现(struct某种程度上用法和对象一样)

int add(int i, int j){return i + j;}
auto mod = [](int i, int j){return i % j;};
struct divide
{int operator()(int m, int n){return m / n;}
};

std::function调用它们的方式如下: 

//初始化
std::function<int(int, int)> f1 = add;
std::function<int(int, int)> f2 = divide();
std::function<int(int, int)> f3 = mod;
//调用
std::cout << f1(4, 2) << std::endl;
std::cout << f2(4, 2) << std::endl;
std::cout << f3(4, 2) << std::endl;

完整C++代码实现: 

#include <iostream>
#include<functional>
int add(int i, int j) { return i + j; }
auto mod = [](int i, int j) {return i % j; };
struct divide
{int operator()(int m, int n){return m / n;}
};
int main()
{std::function<int(int, int)> f1 = add;std::function<int(int, int)> f2 = divide();std::function<int(int, int)> f3 = mod;std::function<int(int, int)> f4 = [](int i, int j) {return i * j; };;std::cout << f1(4, 2) << std::endl;std::cout << f2(4, 2) << std::endl;std::cout << f3(4, 2) << std::endl;std::cout << f4(4, 2) << std::endl;
}

运行结果:

6
2
0
8

3.C++代码样例

Demo_1:

#include <functional>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void execute(const vector<function<void()>>& fs)
{for (auto& f : fs)f();
}
void plain_old_func()
{cout << "I'm an old plain function" << endl;
}
class functor
{
public:void operator()() const{cout << "I'm a functor" << endl;}
};
int main()
{vector<function<void()>> x;x.push_back(plain_old_func);functor functor_instance;x.push_back(functor_instance);x.push_back([](){cout << "I'm a lambda expression" << endl;});execute(x);
}

运行结果: 

I'm an old plain function
I'm a functor
I'm a lambda expression

Demo_2:

#include <iostream>   
#include <functional>   
int main() {// an array of functions:std::function<int(int, int)> fn[] = {std::plus<int>(),std::minus<int>(),std::multiplies<int>()};for (auto& x : fn) {std::cout << x(10, 5) << '\n';}return 0;
}

运行结果:

15
5
50

*补充:头文件functional在C++17标准中引入了std::invoke。

invoke可以不需要经过初始化操作,直接进行调用操作。

std::invoke具体使用方式参考如下代码:

#include <iostream>
#include <functional>
using namespace std;
void globalFunction()
{cout << "globalFunction ..." << endl;
}
class MyClass
{
public:void memberFunction(int data){std::cout << "MyClass memberFunction ..." << std::endl;}static void staticFunction(int data){std::cout << "MyClass staticFunction ..." << std::endl;}
};
int main(
{MyClass obj;std::invoke(&MyClass::memberFunction, obj, 100);std::invoke(&MyClass::staticFunction, 200);std::invoke(globalFunction);return 0;
}

运行结果:

MyClass memberFunction ...
MyClass staticFunction ...
globalFunction ...

三,参考阅读

《Beginning C++17, 5th Edition》
《C++ Primer Plus, 6th Edition》
《The C++ Standard Library, Second Edition》
《C++新经典》
  C/C++开发基础——函数指针&回调函数
https://www.oreilly.com/library/view/mastering-c-programming/
https://oopscenities.net/2012/02/24/c11-stdfunction-and-stdbind/

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

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

相关文章

WM8978 —— 带扬声器驱动程序的立体声编解码器(5)

接前一篇文章&#xff1a;WM8978 —— 带扬声器驱动程序的立体声编解码器&#xff08;4&#xff09; 九、寄存器概览与详解 1. 整体概览 WM8978芯片共有58个寄存器&#xff0c;整体总表如下&#xff1a; 2. 详细说明 在此&#xff0c;只介绍WM8978较为常用的那些寄存器。 &…

C++初阶:string类相关练习题

目录 1. 字符串相加2. 反转字母3. 字符串中唯一字母4. 字符串中最后一个单词5. 验证回文串6. 反转字符II7. 反转字符串中的单词8. 字符串相乘 1. 字符串相加 题目信息&#xff1a; 题目连接&#xff1a; 字符串相加 class Solution { public:string addStrings(string num1, s…

2024龙年新版UI周易测算网站H5源码

支持对接第三方支付 安装方法以linux为例: 1、建议在服务器上面安装宝塔面板,以便操作,高逼格技术员可以忽略这步操作。 2、把安装包文件解压到根目录,同时建立数据库,把数据文件导入数据库 3、修改核心文件config/inc_config.php把数据库信息替换成你的 4、解析域名…

Java 学习和实践笔记(44):数组的声明定义和使用以及初始化

示例代码&#xff1a; public class TestArray {public static void main(String[] args) {/*测试整数型一维数组*/int[] s;//声明s是一个整数数组类型s new int[10];//将s实例化&#xff0c;也就是分配内存空间&#xff0c;真正创建了这个对象//int[] s new int[10];也可以…

Meta 推出SceneScript,一种全新的3D场景重建方式

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Zabbix与Prometheus区别简述

Zabbix与Prometheus区别简述 历史沿革 一、监控工具简介 1、Zabbix https://www.zabbix.com/cn/download Zabbix是传统的监控系统&#xff0c;出现比云原生早&#xff0c;使用的是SQL关系型数据库&#xff1b;开源监控软件&#xff0c;遵守 GPLv2开源协议&#xff0c;起源于…

高架学习笔记之系统分析与设计

目录 一、结构化方法&#xff08;SASD&#xff09; 1.1. 结构化分析方法&#xff08;SA&#xff09; 1.1.1. 数据流图&#xff08;DFD&#xff09; 1.1.2. 实体联系图&#xff08;E-R图&#xff09; 1.1.3. 状态转换图(STD) 1.1.4. 数据字典 1.2. 结构化设计方法&#x…

Prometheus Grafana 配置仪表板

#grafana# 其实grafana提供了丰富的Prometheus数据源的仪表板&#xff0c;基本上主流的都有&#xff0c;通过下面官方地址可查阅 Dashboards | Grafana Labs 这里举例说明&#xff0c;配置node_exporter仪表板 首先&#xff0c;在上面的网站搜索 node 可以查到蛮多的仪表板…

【现代C++】统一初始化

现代C中的统一初始化&#xff08;Uniform Initialization&#xff09;是C11引入的一项特性&#xff0c;它提供了一种统一的语法来初始化任何类型的对象。统一初始化旨在增强代码的一致性和清晰度&#xff0c;减少传统初始化方式中的歧义。以下是统一初始化的几种用法及相应的示…

【贪心】【回溯】【字符串】2014. 重复 K 次的最长子序列

本文涉及知识点 贪心 回溯 字符串 LeetCode2014. 重复 K 次的最长子序列 给你一个长度为 n 的字符串 s &#xff0c;和一个整数 k 。请你找出字符串 s 中 重复 k 次的 最长子序列 。 子序列 是由其他字符串删除某些&#xff08;或不删除&#xff09;字符派生而来的一个字符串…

网络安全笔记-day6,NTFS安全权限

文章目录 NTFS安全权限常用文件系统文件安全权限打开文件安全属性修改文件安全权限1.取消父项继承权限2.添加用户访问权限3.修改用户权限4.验证文件权限5.总结权限 强制继承父项权限文件复制移动权限影响跨分区同分区 总结1.权限累加2.管理员最高权限2.管理员最高权限 NTFS安全…

仿京东项目——京西商城(数据库设计)

文章目录 仿京东——京西商城数据库设计建立E-R图数据库表设计用户表商品表订单表订单详情表评论表购物车表购物车项表 仿京东——京西商城 数据库设计 主要实体有&#xff1a; 用户 用户ID&#xff08;User_ID&#xff09;&#xff1a;唯一标识用户的主键 用户名&#xff0…

同豪BIM模型如何快速修改路面、桥面、梁板颜色

0序 同豪的建模软件&#xff0c;做路桥隧的bim建模&#xff0c;速度很快。缺点就是他们的模型可以认为是没有颜色。只有自带的几种风格、纹理。 部分领导觉得颜色不好看&#xff0c;与背景色对比度不强&#xff0c;甚至甲方也会要求修改模型颜色&#xff0c;使之更醒目&#x…

数字乡村引领新风尚:科技赋能农村实现全面进步

随着信息技术的迅猛发展&#xff0c;数字乡村正成为引领农村全面进步的新风尚。科技作为推动农村发展的强大引擎&#xff0c;正在深刻改变着传统农业的生产方式、农村的社会结构以及农民的生活方式&#xff0c;为农村经济社会的全面进步注入了新的活力和动力。本文将从数字乡村…

JVM—内存可见性

什么是可见性 可见性&#xff1a;一个线程对共享变量值的修改,能够及时地被其他线程看到共享变量&#xff1a;如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JMM) Java内存模型(Java Memory Model)描述了Java程序中各种…

【Java前端技术栈】Vue2、Vue Cli、Axio入门

一、基本介绍 1.Vue 是什么? Vue (读音 /vjuː/&#xff0c;类似于 view) 是一个前端框架, 易于构建用户界面 2. Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或项目整合 3. 支持和其它类库结合使用 4. 开发复杂的单页应用非常方便 5.…

[Rust] 使用vscode实现HelloWorld程序并进行debug

一、简介 本文介绍了如何使用vscode编写rust&#xff0c;实现打印"Hello, world!"的程序。 二、工具安装 0. 环境介绍&#xff1a; Linux &#xff08;或者windowswsl&#xff09; 1. 安装rust编译器rustc和包管理器cargo。 请参考连接&#xff1a;Rust 程序设…

Android iOS客户端自动化UI自动化airtest从0到1搭建macos

一、基础环境 1. 安装jdk 选择jdk8 如果下载高版本 可能不匹配会失败 下载.dmg文件 苹果电脑 &#xff5c; macOS &#xff5c; jdk1.8 &#xff5c; 环境变量配置_jdk1.8 mac-CSDN博客 Java Downloads | Oracle jdk环境变量配置 找到java home qamac ~ % cd /Library/J…

K8s+Nacos实现应用的优雅上下线【生产实践】

文章目录 前言一、环境描述二、模拟请求报错三、配置优雅上下线1.修改nacos配置2.修改depolyment配置3.重新apply deployment后测试4.整体(下单)测试流程验证是否生效 四、期间遇到的问题 前言 我们在使用k8s部署应用的时候&#xff0c;虽然k8s是使用滚动升级的&#xff0c;先…

selenium自动化登录模块HTMLTestRunner测试报告

1.下载HTMLTestRunner.py放到python的Lib目录下&#xff0c;python3之后的&#xff0c;文件要修改以下内容&#xff1a; 第94行&#xff0c;将import StringIO修改成import io 第539行&#xff0c;将self.outputBuffer StringIO.StringIO()修改成self.outputBuffer io.Strin…