【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;
		// 每调用一次, 自增 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
请按任意键继续. . .

在这里插入图片描述

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

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

相关文章

Scikit-Learn线性回归(四)

Scikit-Learn线性回归四:梯度下降 1、梯度下降1.1、梯度下降概述1.2、梯度下降及原理1.3、梯度下降的实现2、梯度下降法求解线性回归的最优解2.1、梯度下降法求解的原理2.2、梯度下降法求解线性回归的最优解2.3、梯度下降法求解线性回归案例(波士顿房价预测)3、Scikit-Learn…

女性需要了解的健康知识

女性需要了解的健康知识 一&#xff1a;脸部 羊大师发现&#xff0c;你的面容不断透露出你的生活态度&#xff0c;可以从一个女人的脸上看出她过得好不好。从脸色是苍白无华还是红润可见一斑&#xff1b;从是否素颜自然还是妆容精致&#xff0c;这一切都显露在他人面前。因此&…

vue接入海康4G版摄像头通过萤石云平台推送视频流教程

需求 最近需要接入海康视频摄像头&#xff0c;然后把视频的画面接入到自己的网站系统中。以前对接过rtsp固定IP的显示视频&#xff0c;这次的不一样&#xff0c;没有了固定IP。海康的解决办法是&#xff0c;摄像头通过配置服务器到萤石云平台&#xff0c;然后购买企业版账号和…

【开源】基于JAVA语言的智能教学资源库系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课程资源模块2.4 课程作业模块2.5 课程评价模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 课程档案表3.2.2 课程资源表3.2.3 课程作业表3.2.4 课程评价表 四、系统展示五、核心代…

Vue CLI初识

脚手架Vue CLI 基本介绍 Vue CLI 是Vue官方提供的一个全局命令工具 可以帮助我们快速创建一个开发Vue项目的标准化基础架子。【集成了webpack配置】 好处 开箱即用&#xff0c;零配置内置babel等工具标准化的webpack配置 使用步骤 全局安装&#xff08;只需安装一次即可…

lenovo联想小新Pro-13 2020 Intel IML版笔记本电脑(82DN)原装出厂Win10系统镜像

链接&#xff1a;https://pan.baidu.com/s/1bJpfXudYEC7MJ7qfjDYPdg?pwdjipj 提取码&#xff1a;jipj 原装出厂Windows10系统自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&a…

机器学习项目标记图像数据 - 安装LabelImg及功能介绍

什么是LabelImg&#xff1f; LabelImg 是一款流行的图像标注工具&#xff0c;主要用于计算机视觉领域。它允许用户为机器学习项目标记图像数据&#xff0c;特别是用于训练目标检测模型。 如何安装LabelImg pip install PyQt5 pip install pyqt5-tools pip install lxml pip …

WPF使用 MahApps.Metro,如何更改MetroWindow标题的字体?

WPF 使用 MahApps.Metro, 如何更改MetroWindow标题的字体&#xff1f; WPF中&#xff0c;在使用 MahApps.Metro后&#xff0c;MetroWindow窗口头部标题的字体默认是大写的&#xff1b;标题显示大写很多时候不是实际需要的&#xff0c;那怎么修改根据XAML输入的内容显示呢&#…

【unity小技巧】实现没有动画的FPS武器摇摆和摆动效果

文章目录 前言开始完结 前言 添加程序摇摆和摆动是为任何FPS游戏添加一些细节的非常简单的方法。但是并不是所以的模型动画都会配有武器摆动动画效果&#xff0c;在本文中&#xff0c;将实现如何使用一些简单的代码实现武器摇摆和摆动效果&#xff0c;这比设置动画来尝试实现类…

数据结构OJ实验14-哈希查找

A. DS哈希查找—线性探测再散列 题目描述 定义哈希函数为H(key) key%11&#xff0c;输入表长&#xff08;大于、等于11&#xff09;。输入关键字集合&#xff0c;用线性探测再散列构建哈希表&#xff0c;并查找给定关键字。 输入 测试次数t 每组测试数据为&#xff1a; …

漫谈大模型的[幻觉]问题

# 如何解决大模型的幻觉问题&#xff1f;# &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;漫谈LLMs带来的AIGC浪潮​​​​​​​ &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质…

人工智能:模拟人类智慧的科技奇迹

人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;作为一项模拟人类智慧行为的科学与技术&#xff0c;正以惊人的速度改变着我们的世界。它旨在让计算机系统具备感知、推理、学习、决策和交互等人类智慧的能力&#xff0c;成为当今科技领域的巨大突破…

卷积神经网络(CNN)、循环神经网络(RNN)和自注意力(self-attention)对比

考虑同一个的问题&#xff1a;将由个词元组成的序列映射到另一个长度相同的序列&#xff0c;其中的每个输入词元或输出词元由维向量表示。 我们将比较能够解决上述问题的三种常用方法&#xff1a;卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#x…

麒麟镜像下载

试用版下载链接 产品试用申请国产操作系统、银河麒麟、中标麒麟、开放麒麟、星光麒麟——麒麟软件官方网站 下载自己对应的操作系统 我下载的是 共享文件下载 - Kylin Distro 然后用迅雷下载就可以了

使用jmeter从0开始完成性能测试

使用JMeter从0开始完成性能测试 介绍 在软件开发过程中&#xff0c;性能测试是一项关键任务&#xff0c;它可以帮助我们评估系统在不同负载条件下的性能表现&#xff0c;发现潜在的性能瓶颈。JMeter是一款功能强大且易于使用的性能测试工具&#xff0c;它可以帮助我们完成各种…

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

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

CTFhub-Web-Web前置技能-“302跳转“

题目信息 HTTP临时重定向&#xff0c;题目截图如下所示&#xff1a; 分析过程 看到跳转路径为&#xff1a;http://challenge-d1a96d97eaecf029.sandbox.ctfhub.com:10800/index.html 分析可能存在重定向问题&#xff0c;如果要想获得flag&#xff0c;则可能存在http://chal…

C练习——鸡兔同笼

题目&#xff1a; 有若干只鸡和兔子在同一个笼子里&#xff0c;从上面数&#xff0c;有98个头&#xff1b;从下面数&#xff0c;有386只脚。问笼中各有几只鸡和兔&#xff1f; 解析&#xff1a; 数学上列二元一次方程组求解&#xff0c;所以采用穷举法&#xff0c;但可以缩小…

PyTorch基础操作

一、Tensor 在 PyTorch 中&#xff0c;张量&#xff08;Tensor&#xff09;是一个核心概念&#xff0c;它是一个用于存储和操作数据的多维数组&#xff0c;类似于 NumPy 的 ndarray&#xff0c;但与此同时&#xff0c;它也支持 GPU 加速&#xff0c;这使得在大规模数据上进行科…

Fontfabric:一款字体与设计的完美结合

一、产品介绍 Fontfabric是一款由国际字体设计公司Fontfabric开发的字体设计软件。它提供了一整套完整的字体设计工具&#xff0c;让用户可以轻松地创建、设计和定制自己的字体。Fontfabric拥有丰富的字体库&#xff0c;包括各种风格和类型&#xff0c;能够满足用户在不同场景…