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

news2024/11/23 23:08:10

文章目录

  • 一、函数对象中存储状态
    • 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;
		// 每调用一次, 自增 1
		n++;
	}

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;
		// 每调用一次, 自增 1
		n++;
	}

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;
		// 每调用一次, 自增 1
		n++;
	}

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;
		// 每调用一次, 自增 1
		n++;
	}

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;
		// 每调用一次, 自增 1
		n++;
	}

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.coloradmin.cn/o/1361250.html

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

相关文章

【开源项目】WPF 扩展组件 -- Com.Gitusme.Net.Extensiones.Wpf

一、项目简介 Com.Gitusme.Net.Extensiones.Wpf 是一款 Wpf 扩展组件。基于.Net Core 3.1 开发&#xff0c;当前最新 1.0.1 版本。包含 核心扩展库&#xff08;Com.Gitusme.Net.Extensiones.Core&#xff09;、视频渲染&#xff08;Com.Gitusme.Media.Video&#xff09;、串口…

苹果Mac图像修图软件Photomator和Pixelmator Pro 有什么区别?

同为一个团队设计的Mac修图软件Photomator和Pixelmator Pro有哪些区别呢&#xff1f;有哪些不一样的功能&#xff1f; Photomator和Pixelmator Pro区别如下&#xff1a; 1、用途不同 Photomator 和 Pixelmator Pro 是两个功能强大的应用程序&#xff0c;具有两个不同的用途。…

从Eumetsat批量下载哨兵数据等各种数据

从Eumetsat批量下载哨兵数据等各种数据 那些最好的程序员不是为了得到更高的薪水或者得到公众的仰慕而编程&#xff0c;他们只是觉得这是一件有趣的事情&#xff01; 批量下载Sentinel数据脚本2023 从Eumetsat批量下载哨兵数据等各种数据&#x1f33f;前言&#x1f340;脚本构成…

原生微信小程序如何动态修改svg图片颜色及尺寸、宽高(封装svgIcon组件)解决ios不显示问题

最终效果 前言 动态设置Svg图片颜色就是修改Svg源码的path中的fill属性&#xff0c; 通过wx.getFileSystemManager().readFile读取.xlsx文件 ios不显示需要把encoding设置 binary 把文件转成base64 封装svg-icon组件 1、在项目的components下新建svg-icon文件夹&#xff0c;新…

no usable temporary directory found in %s“ % dirlist 问题解决

提示其实就是没有可用空间&#xff0c;那我们就找到占用空间大且无用的数据文件删除掉 du -sh * 删除掉/tmp目录下的文件。 重启 问题解决

cpufreq子系统

cpufreq是linux上负责实现动态调频的关键&#xff0c;这篇笔记总结了linux内核cpufreq子系统的关键实现&#xff08;Linux 3.18.140&#xff09;。 概述 借用一张网络上的图片来看cpufreq子系统的整体结构&#xff1a; 用户态接口&#xff1a;cpufreq通过sysfs向用户态暴露接…

2022年多元统计分析期中试卷

多元正态均值检验 一、去年卖出的一岁牛犊的平均身高为 51 英寸&#xff0c;平均背脂厚度是 0.3 英寸&#xff0c;平均肩高是 56 英寸。已知今年卖出的 76 头一岁牛犊的 3 项平均指标为(50, 0.2, 54)‘&#xff0c;样本协差阵及其逆矩阵为 S [ 3.00 − 0.053 2.97 − 0.053 0…

【Bootstrap5学习 day12】

Bootstrap5 导航 Bootstrap5提供了一种简单快捷的方法来创建基本导航&#xff0c;它提供了非常灵活和优雅的选项卡和Pills等组件。Bootstrap5的所有导航组件&#xff0c;包括选项卡和Pillss&#xff0c;都通过基本的.nav类共享相同的基本标记和样式。 创建基本导航 要创建简单…

eureka注册列表 某服务出现多个服务实例

最近文件导出功能偶发成功&#xff0c;大部分情况都失败&#xff0c;开始以为接口被拦截&#xff0c;gateway服务没有接口调用日志&#xff0c;发现测试环境可以&#xff0c;正式环境功能无法正常使用。 偶然看到注册中心如下 发现file服务有3个实例&#xff0c;调用接口将错误…

Java十种经典排序算法详解与应用

数组的排序 前言 排序概念 排序是将一组数据&#xff0c;依据指定的顺序进行排列的过程。 排序是算法中的一部分&#xff0c;也叫排序算法。算法处理数据&#xff0c;而数据的处理最好是要找到他们的规律&#xff0c;这个规律中有很大一部分就是要进行排序&#xff0c;所以需…

Excel中快速隐藏中间四位手机号或者身份证号等

注意&#xff1a;以下方式必须再新增一列&#xff0c;配合旧的一列用来对比操作&#xff0c;即根据旧的一列的数据源&#xff0c;通过新的一列的操作逻辑来生成新的隐藏数据 1、快捷方式是使用CtrlE 新建一列&#xff1a;手动输入第一个手机号隐藏后的号码&#xff0c;即在N2单…

VS+QT五子棋游戏开发

1、首先安装好VS软件和QT库&#xff0c;将其配置好&#xff0c;具体不在此展开说明。 2、文件结构如下图&#xff1a; 3、绘制棋盘代码&#xff0c;如下&#xff1a; void Qwzq::paintEvent(QPaintEvent* event) {QPainter painter(this);painter.setRenderHint(QPainter::An…

Unity之键盘鼠标的监控

小编最近在玩大表哥2&#xff0c;通过 W、A、S、D 来移动亚瑟&#xff0c;鼠标左键来不吃牛肉 我们都知道玩家通过按键鼠标来控制游戏人物做出相应的行为动作&#xff0c;那在Unity引擎里是怎么知道玩家是如何操作的呢&#xff1f;本篇来介绍Unity是怎样监控键盘和鼠标的。 首先…

智创有术开发公司,星潮宇宙开发。

2024年&#xff0c;星潮宇宙全网首发&#xff0c;一个全新的赛道将被开启&#xff0c;这将对游戏产业产生深远的影响。本文将深入探讨这场首发的对接团队以及他们的突破性举措&#xff0c;以展示其对游戏界的重要意义。 星潮宇宙全网首发概述 星潮宇宙的全网首发意味着将有一…

Cloud模型matlab

学习资料python 多维正态云python 预备知识&#xff1a; 如何获取具有特定均值和方差的正态分布随机数。首先&#xff0c;初始化随机数生成器&#xff0c;以使本示例中的结果具备可重复性。 rng(0,twister);基于均值为 500 且标准差为 5 的正态分布创建包含 1000 个随机值的向…

损失函数篇 | YOLOv8 引入 Shape-IoU 考虑边框形状与尺度的度量

作者导读&#xff1a;Shape-IoU&#xff1a;考虑边框形状与尺度的度量 论文地址&#xff1a;https://arxiv.org/abs/2312.17663 作者视频解读&#xff1a;https://www.bilibili.com 开源代码地址&#xff1a;https://github.com/malagoutou/Shape-IoU/blob/main/shapeiou.py…

Linux第16步_安装NFS服务

NFS&#xff08;Network File System&#xff09;是一种在网络上实现的分布式文件系统&#xff0c;它允许不同的操作系统和设备之间共享文件和资源。 在创建的linux目录下&#xff0c;再创建一个“nfs“文件夹&#xff0c;用来供nfs服务器使用&#xff0c;便于”我们的开发板“…

Android Studio 模拟器卡死的解决方法!

目录 前言 一、常规方法 二、简便解决方法 前言 在开发过程中&#xff0c;使用Android Studio模拟器是一种常见的方式来测试和调试应用程序。然而&#xff0c;有时候你可能会遇到模拟器卡死的情况&#xff0c;这给开发工作带来了一定的困扰。模拟器卡死可能会让你无法正常进…

设计模式——迭代器模式(Iterator Pattern)

概述 迭代器模式(Iterator Pattern)&#xff1a;提供一种方法来访问聚合对象&#xff0c;而不用暴露这个对象的内部表示&#xff0c;其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。 在软件开发中&#xff0c;我们经常需要使用聚合对象来存储一系列数据。聚合对象拥有…

密码学入门 古老的围栏密码技术

1、简述 由于隐私和安全的重要性不断增加&#xff0c;已经开发了多种加密方法和技术来保护我们的敏感数据。随着时间的推移而演变&#xff0c;从经典密码学发展到现代密码学。 在本文中&#xff0c;我们将了解一种被称为围栏密码技术的技术&#xff0c;涵盖其加密和解密过程及其…