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,一经查实,立即删除!

相关文章

1.1-数组-704. 二分查找★

704. 二分查找 ★ 力扣题目链接&#xff0c;给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;搜索 nums 中的 target&#xff0c;如果存在返回下标&#xff0c;否则返回 -1。n 将在 [1, 10000]之间。 可以假设 nums 中的所…

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…

公司管理-技术VS市场

技术型公司往往会出现这样的场景&#xff1a;市场人员抱怨产品差不好卖&#xff1b;技术人员不给力&#xff0c;项目总验收不了。技术人员抱怨市场人员只管签单&#xff0c;什么都敢答应。这就是矛盾&#xff0c;应该应用矛盾论来解决问题。 产品的发展需要时间、人力的不断投入…

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

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

【C语言基础篇】内存处理函数(三)memset的介绍及模拟实现

个人主页&#xff1a; 倔强的石头的博客 系列专栏 &#xff1a;C语言指南 C语言刷题系列 待补充完善

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];也可以…

如何设计一个可扩展的Web应用架构?

如何设计一个可扩展的Web应用架构&#xff1f; 设计一个可扩展的Web应用架构是一个复杂而重要的任务&#xff0c;需要综合考虑多个方面&#xff0c;包括技术选型、系统架构、性能优化等。以下是一些关键步骤和策略&#xff0c;可以帮助您设计一个可扩展的Web应用架构&#xff…

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

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

【洛谷 P8709】[蓝桥杯 2020 省 A1] 超级胶水 题解(贪心算法+优先队列+哈夫曼树)

[蓝桥杯 2020 省 A1] 超级胶水 题目描述 小明有 n n n 颗石子&#xff0c;按顺序摆成一排&#xff0c;他准备用胶水将这些石子粘在一起。 每颗石子有自己的重量&#xff0c;如果将两颗石子粘在一起&#xff0c;将合并成一颗新的石子&#xff0c;重量是这两颗石子的重量之和…

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…

【wpf应用8】如何让WPF Grid控件根据屏幕尺寸自动调整

简介&#xff1a; 在Windows Presentation Foundation&#xff08;WPF&#xff09;中&#xff0c;Grid控件是一个强大的布局工具&#xff0c;它允许开发者创建复杂且响应迅速的用户界面。在不同的设备和屏幕尺寸上保持良好的布局一致性是一个挑战。本文将介绍如何让Grid控件根据…

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;字符派生而来的一个字符串…

小白DB补全计划Day2-LeetCode:SQL基本操作selectJOIN

链接&#xff1a;1683. 无效的推文 - 力扣&#xff08;LeetCode&#xff09;1378. 使用唯一标识码替换员工ID - 力扣&#xff08;LeetCode&#xff09;1068. 产品销售分析 I - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 1683 # Write your MySQL quer…

网络安全笔记-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…