【C++】STL 算法 ③ ( 函数对象中存储状态 | 函数对象作为参数传递时值传递问题 | for_each 算法的 函数对象 参数是值传递 )

文章目录

  • 一、函数对象中存储状态
    • 1、函数对象中存储状态简介
    • 2、示例分析
  • 二、函数对象作为参数传递时值传递问题
    • 1、for_each 算法的 函数对象 参数是值传递
    • 2、代码示例 - for_each 函数的 函数对象 参数在外部不保留状态
    • 3、代码示例 - for_each 函数的 函数对象 返回值





一、函数对象中存储状态



1、函数对象中存储状态简介


在 C++ 语言中 , 函数对象 / 仿函数 可以像函数一样被调用 , 并且 其 还具有类的特征 , 可以 通过 继承 和 重载 来 修改 重载函数调用操作符函数 的行为 ;

函数对象 / 仿函数 通常是通过 定义一个类 , 然后为这个类 重载 函数调用操作符 () 来实现的 ;

函数对象的一个重要特性是 " 可以存储状态 " ; 这意味着你可以 在类的成员变量中存储数据 , 这些数据可以 在函数调用之间保持不变 ;

普通的函数 是 无法存储状态 的 , 因为 普通函数 中 局部变量 在函数执行完成后 , 自动销毁 ;


函数对象 / 仿函数 的一个主要优势是它们可以拥有状态 , 而普通函数则不能 ;

这使得 " 函数对象 / 仿函数 " 在需要保持 某些数据或状态 在 多次函数调用 之间不变的情况下非常有用 ,

例如 : 在 STL 算法中 , 函数对象经常被用作 谓词 或 用于在容器的每个元素上执行某种操作的函数 , 由于它们可以存储状态 , 因此可以根据算法的需要进行定制 ;

在下面的示例中 , 函数对象 中 维护了一个状态位 , 用于记录该 函数对象 的调用次数 ;


下面的 函数对象 / 仿函数 中 , 存储了状态 n , 每调用一次该仿函数 , 该成员自增 1 ;

//函数对象 类重载了()
template <typename T>
class PrintT {
public:void operator()(T& t) {cout << n << " . " << t << endl;// 每调用一次, 自增 1n++;}private:// 每调用一次, 该成员自增 1// 该状态一直存储int n = 0;
};

2、示例分析


在下面的代码示例中 ,

首先 , 定义了 函数对象 / 仿函数 PrintT 类 , 该类 重载了 函数调用操作符 () , 其重载函数是 void operator()(T& t) ;

  • 在该 函数对象 中 , 存储了一个状态值 n ,
  • 每次调用该 重载函数 , 状态值 n 都会自增 1 ;
//函数对象 类重载了()
template <typename T>
class PrintT {
public:void operator()(T& t) {cout << n << " . " << t << endl;// 每调用一次, 自增 1n++;}private:// 每调用一次, 该成员自增 1// 该状态一直存储int n = 0;
};

然后 , 在 foreach 循环中 , 将该 函数对象 传入 循环算法 中 , 每次遍历 vector 容器中的元素时 , 都会调用 该 函数对象 , 同时 每次调用 时 , 函数对象中的 n 值都会自增 1 ;

	// 向 foreach 循环中传入函数对象// 在函数对象中打印元素内容for_each(vec.begin(), vec.end(), PrintT<int>());

代码示例 :

#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"//函数对象 类重载了()
template <typename T>
class PrintT {
public:void operator()(T& t) {cout << n << " . " << t << endl;// 每调用一次, 自增 1n++;}private:// 每调用一次, 该成员自增 1// 该状态一直存储int n = 0;
};int main() {// 创建一个 vector 单端数组容器vector<int> vec;// 向容器中插入元素vec.push_back(1);vec.push_back(3);vec.push_back(5);// 向 foreach 循环中传入函数对象// 在函数对象中打印元素内容for_each(vec.begin(), vec.end(), PrintT<int>());// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
};

执行结果 : 打印时 , 先把状态值 n 打印出来 , 然后跟着打印 vector 容器中的元素 ,

0 . 1
1 . 3
2 . 5
请按任意键继续. . .

在这里插入图片描述





二、函数对象作为参数传递时值传递问题



1、for_each 算法的 函数对象 参数是值传递


下面开始分析 for_each 函数中 函数对象 作为参数的 具体细节 ;

for_each 算法的调用代码如下 :

	// 向 foreach 循环中传入函数对象// 在函数对象中打印元素内容for_each(vec.begin(), vec.end(), PrintT<int>());

for_each 算法的函数原型如下 :

// FUNCTION TEMPLATE for_each
template <class _InIt, class _Fn>
_Fn for_each(_InIt _First, _InIt _Last, _Fn _Func) { // perform function for each element [_First, _Last)_Adl_verify_range(_First, _Last);auto _UFirst      = _Get_unwrapped(_First);const auto _ULast = _Get_unwrapped(_Last);for (; _UFirst != _ULast; ++_UFirst) {_Func(*_UFirst);}return _Func;
}

上述 for_each 函数的 形参 _Fn _Func 是一个 值 , 不是引用 ;

传递的是 引用 的话 , 那么 外部的对象 和 实参值 是相同的对象 ;

传递的是 值 的话 , 那么 实参 只是 外部的对象 的 副本值 , 在 for_each 函数中 , 无论如何操作改变实参 , 都不会影响到 外部的对象 ;


如果 在 for_each 算法中 调用了 函数对象 , 函数对象中 有 状态改变 ;

在 for_each 算法 外部 继续调用该 函数对象 , 由于 for_each 是 值传递 , 传递的 只是 函数对象副本 , 副本的 状态改变 不会影响到外部函数 ;

如果想要 保留上述 状态改变 , 则需要使用 函数对象 接收 for_each 的返回值 , 这个函数对象 保留了 内部 函数对象参数副本 的状态值 ;


2、代码示例 - for_each 函数的 函数对象 参数在外部不保留状态


如果 在 for_each 算法中 调用了 函数对象 , 函数对象中 有 状态改变 ;

在 for_each 算法 外部 继续调用该 函数对象 , 由于 for_each 是 值传递 , 传递的 只是 函数对象副本 , 副本的 状态改变 不会影响到外部函数 ;


在外部调用 函数对象 时 , 发现状态值 还是 0 , 这说明 值传递 改变的是 函数对象实参副本值 , 没有影响外部的 函数对象 值 ;

0 . 666

代码示例 :

#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"//函数对象 类重载了()
template <typename T>
class PrintT {
public:void operator()(T& t) {cout << n << " . " << t << endl;// 每调用一次, 自增 1n++;}private:// 每调用一次, 该成员自增 1// 该状态一直存储int n = 0;
};int main() {// 创建一个 vector 单端数组容器vector<int> vec;// 向容器中插入元素vec.push_back(1);vec.push_back(3);vec.push_back(5);// 创建函数对象PrintT<int> printT;// 向 foreach 循环中传入函数对象// 在函数对象中打印元素内容for_each(vec.begin(), vec.end(), printT);// 再次调用 函数对象cout << "再次调用函数对象 : " << endl;int a = 666;printT(a);// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
};

执行结果 :

0 . 1
1 . 3
2 . 5
再次调用函数对象 :
0 . 666
请按任意键继续. . .

在这里插入图片描述


3、代码示例 - for_each 函数的 函数对象 返回值


如果 在 for_each 算法中 调用了 函数对象 , 函数对象中 有 状态改变 ;

在 for_each 算法 外部 继续调用该 函数对象 , 由于 for_each 是 值传递 , 传递的 只是 函数对象副本 , 副本的 状态改变 不会影响到外部函数 ;

如果想要 保留上述 状态改变 , 则需要使用 函数对象 接收 for_each 的返回值 , 这个函数对象 保留了 内部 函数对象参数副本 的状态值 ;


使用 PrintT<int> printT; 函数对象 变量 , 接收 for_each 算法的返回值 , 再次执行该 函数对象 调用 , 发现 状态值被保留了下来 , 打印值为 :

3 . 666

代码示例 :

#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"//函数对象 类重载了()
template <typename T>
class PrintT {
public:void operator()(T& t) {cout << n << " . " << t << endl;// 每调用一次, 自增 1n++;}private:// 每调用一次, 该成员自增 1// 该状态一直存储int n = 0;
};int main() {// 创建一个 vector 单端数组容器vector<int> vec;// 向容器中插入元素vec.push_back(1);vec.push_back(3);vec.push_back(5);// 创建函数对象PrintT<int> printT;// 向 foreach 循环中传入函数对象// 在函数对象中打印元素内容printT = for_each(vec.begin(), vec.end(), printT);// 再次调用 函数对象cout << "再次调用函数对象 : " << endl;int a = 666;printT(a);// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
};

执行结果 :

0 . 1
1 . 3
2 . 5
再次调用函数对象 :
3 . 666
请按任意键继续. . .

在这里插入图片描述

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

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

相关文章

权威认可!甄知科技猪齿鱼产品荣获信创产品评估证书

近日&#xff0c;依据《信息技术应用创新产品评估规范 第1部分&#xff1a;应用软件》&#xff08;T/SSIA 2001-2022&#xff09;&#xff0c;经过严格评估&#xff0c;甄知科技旗下自主研发的猪齿鱼数智化开发管理平台 V2.0.0&#xff0c;通过信创测试认证&#xff0c;获得上海…

差分约束算法

差分约束 差分约束系统包含 m m m个涉及 n n n个变量的差额限制条件&#xff0c;这些差额限制条件每个都是形式为 x i − x j ≤ b ∈ [ 1 , m ] x_i-x_j\leq b_{\in[1,m]} xi​−xj​≤b∈[1,m]​的简单线性不等式。 通常我们要求解出一组可行解。 最短路差分约束 如果我们…

ubuntu 22 virt-manger(kvm)安装winxp; ubuntu22体验 firebird3.0

安装 、启动 virt-manager sudo apt install virt-manager sudo systemctl start libvirtdsudo virt-manager安装windowsXP 安装过程截图如下 要点1 启用 “包括寿终正寝的操作系统” win_xp.iso 安装过程 &#xff1a; 从winXp.iso启动, 执行完自己重启从硬盘重启&#xff0c…

稿件代写3个不可或缺的步骤让你事半功倍-华媒舍

作为一个需求频繁的作者&#xff0c;你可能会面临大量的稿件代写任务。但是&#xff0c;你是否曾经为提高文章质量而苦恼过&#xff1f;是否希望在有限的时间内完成更多的代写任务&#xff1f;本篇文章将向你介绍三个不可或缺的稿件代写步骤&#xff0c;帮助你事半功倍&#xf…

Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

Redis的慢查询 许多存储系统(例如 MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似…

Huggy Lingo: 利用机器学习改进 Hugging Face Hub 上的语言元数据

太长不看版: Hub 上有不少数据集没有语言元数据&#xff0c;我们用机器学习来检测其语言&#xff0c;并使用 librarian-bots 自动向这些数据集提 PR 以添加其语言元数据。 Hugging Face Hub 已成为社区共享机器学习模型、数据集以及应用的存储库。随着 Hub 上的数据集越来越多&…

[概率论]四小时不挂猴博士

贝叶斯公式是什么 贝叶斯公式是概率论中的一个重要定理&#xff0c;用于计算在已知一些先验信息的情况下&#xff0c;更新对事件发生概率的估计。贝叶斯公式的表达式如下&#xff1a; P(A|B) P(B|A) * P(A) / P(B) 其中&#xff0c;P(A|B)表示在事件B发生的条件下事件A发生的概…

基于sumo实现交通灯控制算法的模板

基于sumo实现交通灯控制算法的模板 目录 在windows安装run hello world networkroutesviewsettings & configurationsimulation 交通灯控制系统 介绍文件生成器类&#xff08;FileGenerator&#xff09;道路网络&#xff08;Network&#xff09;辅助函数生成道路网络&am…

php 数组中的元素进行排列组合

需求背景&#xff1a;计算出数组[A,B,C,D]各种排列组合&#xff0c;希望得到的是数据如下图 直接上代码&#xff1a; private function finish_combination($array, &$groupResult [], $splite ,){$result [];$finish_result [];$this->diffArrayItems($array, $…

12、DolphinScheduler

1、DolphinScheduler简介 1.1、 DolphinScheduler概述 Apache DolphinScheduler是一个分布式、易扩展的可视化DAG工作流任务调度平台。致力于解决数据处理流程中错综复杂的依赖关系&#xff0c;使调度系统在数据处理流程中开箱即用。 1.2、 DolphinScheduler核心架构 Dolph…

USB -- STM32F103缓冲区描述表及USB数据存放位置讲解(续)

目录 链接快速定位 前沿 1 0x40005C00和0x40006000地址的区别和联系 2 USB_BTABLE寄存器介绍 3 USB缓冲区描述表&#xff08;SRAM&#xff09;介绍 3.1 发送缓冲区地址寄存器n&#xff08;n[0..7]&#xff09; 3.2 发送数据字节数寄存器n&#xff08;n[0..7]&#xff09…

从C++习题中思考

目录 一.开始1.1 二.变量和基本类型1.11.21.31.31.41.5 C Peimer习题集第5版练习。 一.开始 1.1 编写程序&#xff0c;提示用户输入2个整数&#xff0c;打印出这两个整数指定的范围内的所有整数。 方式1&#xff1a;使用while循环。 #include<iostream> using namespac…

socket实现视频通话-WebRTC

最近喜欢研究视频流&#xff0c;所以思考了双向通信socket&#xff0c;接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~ 客户端获取视频流 首先思考如何获取视频流呢&#xff1f; 其实跟录音的功能差不多&#xff0c;都是查询电脑上是否有媒体设备&#xff0c;如果…

C语言学习NO.11-字符函数strlen,strlen函数的使用,与三种strlen函数的模拟实现

&#xff08;一&#xff09;strlen函数的使用 strlen函数的演示 #include <stdio.h> #include <string.h>int main() {char arr1[] "abcdef";char arr2[] "good";printf("arr1 %d,arr2 %d",strlen(arr1),strlen(arr2));return …

GUI三维绘图

绘制三维图plot3 t0:pi/50:10*pi; xsin(t); ycos(t); zt; plot3(x,y,z); 产生栅格数据点meshgrid 这个接口在绘制三维图像里面相当重要&#xff0c;很多时候要将向量变成矩阵才能绘制三维图。 x0:0.5:5; y0:1:10; [X,Y]meshgrid(x,y); plot(X,Y,o); x和y是向量&#xff0c;…

Python开发环境搭建

Python程序设计语言是解释型语言&#xff0c;其广泛应用于运维开发领域、数据分析领域、人工智能领域&#xff0c;本文主要描述Python开发环境的搭建。 www.python.org 如上所示&#xff0c;从官方网站下载Python最新的稳定版本3.12.1 如上所示&#xff0c;在本地的开发环境安…

Spring面试篇

Spring面试篇 前置知识ApplicationContextInitializerApplicationListenerBeanFactoryBeanDefinitionBeanFactoryPostProcesssorAwareInitialzingBean&#xff0c;DisposableBeanBeanPostProcessor SpringBoot启动流程IOC容器初始化流程Bean生命周期Bean循环依赖解决 SpringMvc…

关于kthread_stop的疑问(linux3.16)

线程一旦启动起来后&#xff0c;会一直运行&#xff0c;除非该线程主动调用do_exit函数&#xff0c;或者其他的进程调用kthread_stop函数&#xff0c;结束线程的运行。 之前找销毁内核线程的接口时&#xff0c;发现了kthread_stop这个接口。网上说这个函数能够销毁一个内核线程…

Linux 的引导与服务控制

一 开机启动过程 bios加电自检-->mbr-->grub-->加载内核文件-->启动第一个进程 1 bios加电自检 检测硬件是否正常&#xff0c;然后根据bios中的启动项设置&#xff0c;去找内核文件 2 mbr 因为grup太大,第一个扇区存不下所有的grub程序&#xff0c;所以分为…

【Tools】VS基本使用

文章目录 0 前言1 下载安装与基本使用1.1 下载安装1.2 项目创建1.3 编译运行和调试1.4 界面和设置 2 项目属性配置【重点】2.1 打开项目属性配置窗口2.2 静态库和动态库2.3 包含目录&库目录&依赖项&工作目录2.4 代码中添加附加依赖项2.5 配置项目环境变量2.6 修改属…