C++ 函数对象

如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。函数对象是一个对象,但是使用的形式看起来像函数调用,实际上也执行了函数调用,因而得名。

函数对象的例子。

#include <iostream>
using namespace std;class CAverage
{
public:double operator()(int a1, int a2, int a3){  //重载()运算符return (double)(a1 + a2 + a3) / 3;}
};int main()
{CAverage average;  //能够求三个整数平均数的函数对象cout << average(3, 2, 3);  //等价于 cout << average.operator(3, 2, 3);return 0;
}

程序的输出结果是:

2. 66667

()是目数不限的运算符,因此重载为成员函数时,有多少个参数都可以。

average 是一个对象,average(3, 2, 3) 实际上就是 average.operator(3, 2, 3),这使得 average 看上去像函数的名字,故称其为函数对象。

函数对象在 accumulate 算法中的应用

STL 中有以下实现“累加”功能的算法(函数模板):

template <class InIt, class T, class Pred>
T accumulate(InIt first, InIt last, T val, Pred op);

该模板的功能是对 [first, last) 中的每个迭代器 I 执行 val = op(val, *I),返回最终的 val。在 Dev C++ 中,numeric 头文件中 accumulate 的源代码如下:

template <class InIt, class T, class Pred>
T accumulate(InIt first, Init last, T init, Pred op)
{for (; first != last; ++first)init = op(init, *first);return init;
};

此模板被实例化后,op(init, *first)必须要有定义,则 op 只能是函数指针或者函数对象。因此调用该 accmulate 模板时,形参 op 对应的实参只能是函数名、函数指针或者函数对象。

下面的程序通过 accumulate 模板求一个 vector 中元素的平方和,其中用到了函数对象。

#include <iostream>
#include <vector>
#include <numeric> //accumulate 在此头文件定义
using namespace std;
template <class T>
void PrintInterval(T first, T last)
{ //输出区间[first,last)中的元素for (; first != last; ++first)cout << *first << " ";cout << endl;
}
int SumSquares(int total, int value)
{return total + value * value;
}
template<class T>
class SumPowers
{
private:int power;
public:SumPowers(int p) :power(p) { }const T operator() (const T & total, const T & value){ //计算 value的power次方,加到total上T v = value;for (int i = 0; i < power - 1; ++i)v = v * value;return total + v;}
};
int main()
{const int SIZE = 10;int a1[] = { 1,2,3,4,5,6,7,8,9,10 };vector<int> v(a1, a1 + SIZE);cout << "1) "; PrintInterval(v.begin(), v.end());int result = accumulate(v.begin(), v.end(), 0, SumSquares);cout << "2) 平方和:" << result << endl;result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));cout << "3) 立方和:" << result << endl;result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));cout << "4) 4次方和:" << result;return 0;
}

程序的输出结果如下:

1 2 3 4 5 6 7 8 9 10
平方和:385
立方和3025
4次方和:25333

第 37 行,第四个参数是 SumSquares 函数的名字。函数名字的类型是函数指针,因此本行将 accumulate 模板实例化后得到的模板函数定义如下:

int accumulate(vector <int>::iterator first, vector <int>::iterator last, int init, int(*op)(int, int))
{for (; first != last; ++first)init = op(init, *first);return init;
}

形参 op 是一个函数指针,而op(init, *first)就调用了指针 op 指向的函数,在第 37 行的情况下就是函数 SumSquares。

第 39 行,第四个参数是 SumPowers(3)。SumPowers 是类模板的名字,SumPowers 就是类的名字。类的名字后面跟着构造函数的参数列表,就代表一个临时对象。因此 SumPowers(3) 就是一个 SumPowers 类的临时对象。

编译器在编译此行时,会将 accumulate 模板实例化成以下函数:

int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, SumPowers<int> op)
{for (; first != last; ++first)init = op(init, *first);return init;
}

形参 op 是一个函数对象,而op(init, *first)等价于:
op.operator()(init, *first);
即调用了 SumPowers 类的 operator() 成员函数。

对比 SumPowers 和 SumSquares 可以发现,函数对象的 operator() 成员函数可以根据对象内部的不同状态执行不同操作,而普通函数就无法做到这一点。因此函数对象的功能比普通函数更强大。

函数对象在sort算法中的应用

STL 中的排序模板 sort 能将区间从小到大排序。sort 算法有两个版本。第一个版本的原型如下:

template <class_Randlt>
void sort(_Randlt first, _RandIt last);

该模板可以用来将区间 [first, last) 中的元素从小到大排序,要求 first、last 是随机访问迭代器。元素比较大小是用<进行的。如果表达式a<b的值为 true,则 a 排在 b 前面;如果a<b的值为 false,则 b 未必排在 a 前面,还要看b<a是否成立,成立的话 b 才排在 a 前面。要使用这个版本的 sort 算法,待排序的对象必须能用<运算符进行比较。

sort 算法第二个版本的原型如下:

template <class_Randlt, class Pred>
void sort(_Randlt first, _RandIt last, Pred op);

这个版本和第一个版本的差别在于,元素 a、b 比较大小是通过表达式op(a, b)进行的。如果该表达式的值为 true,则 a 比 b 小;如果该表达式的值为 false,也不能认为 b 比 a 小,还要看op(b, a)的值。总之,op 定义了元素比较大小的规则。下面是一个使用 sort 算法的例子。

#include <iostream>
#include <algorithm>  //sort算法在此头文件中定义
using namespace std;
template <class T>
void Printlnterva1(T first, T last)
{  //用以输出 [first, last) 区间中的元素for (; first != last; ++first)cout << *first << " ";cout << endl;
}
class A
{
public:int v;A(int n) : v(n) {}
};
bool operator < (const A & a1, const A & a2)
{  //重载为 A 的 const 成员函数也可以,重载为非 const 成员函数在某些编译器上会出错return a1.v < a2.v;
}
bool GreaterA(const A & a1, const A & a2)
{  //v值大的元素作为较小的数return a1.v > a2.v;
}
struct LessA
{bool operator() (const A & a1, const A & a2){  //v的个位数小的元素就作为较小的数return (a1.v % 10) < (a2.v % 10);}
};
ostream & operator << (ostream & o, const A & a)
{o << a.v;return o;
}
int main()
{int a1[4] = { 5, 2, 4, 1 };A a2[5] = { 13, 12, 9, 8, 16 };sort(a1, a1 + 4);cout << "1)"; Printlnterva1(a1, a1 + 4);  //输出 1)1 2 4 5sort(a2, a2 + 5);  //按v的值从小到大排序cout << "2)"; Printlnterva1(a2, a2 + 5);  //输出 2)8 9 12 13 16sort(a2, a2 + 5, GreaterA);  //按v的值从大到小排序cout << "3)"; Printlnterva1(a2, a2 + 5);  //输出 3)16 13 12 9 8sort(a2, a2 + 5, LessA());  //按v的个位数从小到大排序cout << "4)"; Printlnterva1(a2, a2 + 5);  //输出 4)12 13 16 8 9return 0;
}

编译至第 45 行时,编译器将 sort 实例化得到的函数原型如下:

void sort(A* first, A* last, bool (*op)(const A &, const A &) );

该函数在执行过程中,当要比较两个元素 a、b 的大小时,就是看 op(a, b) 和 op(b, a) 的返回值。本程序中 op 指向 GreaterA,因此就用 GreaterA 定义的规则来比较大小。

编译至第 47 行时,编译器将 sort 实例化得到的函数原型如下:

void sort( A* first, A* last, LessA op);

该函数在执行过程中,当要比较两个元素 a、b 的大小时,就是看 op(a, b) 和 op(b, a) 的返回值。本程序中,op(a, b) 等价于 op.opeartor(a, b),因此就用 LessA 定义的规则来比较大小。

STL 中定义了一些函数对象类模板,都位于头文件 functional 中。例如,greater 模板的源代码如下:

template <class T>
struct greater
{bool operator()(const T& x, const T& y) const{return x > y;}
};

假设有以下数组:

int a[4] = {3, 5, 34, 8};

要将该数组从大到小排序,则只需写:

sort( a, a+4, greater<int>() );

要使用 greater 模板,须确保>运算符本来就有定义,或经过了适当的重载。

list 容器的 sort 成员能将元素从小到大排序。它也有两个版本:一个是没有参数的函数,比较大小用<运算符;另一个是函数模板,原型如下:

template <class Pred>
void sort(Pred op);

sort 函数允许自定义比较大小的规则,即 op(x, y) 为真就认为 x 比 y 小。例如,假设有:

list<int> lst;

如果希望将 lst 中的元素按其整数数值从大到小排序,只需写:

lst.sort( greater<int>() );

在使用关联容器和许多算法时,都可以用函数对象来定义比较大小的规则,以及其他一些规则和操作。

STL 中的函数对象类模板

STL 中的函数对象类模板

函数对象类模板成员函数 T operator ( const T & x, const T & y) 的功能
plus return x + y;
minus < >return x - y;
multiplies return x * y;
divides return x / y;
modulus return x % y;
equal_to return x == y;
not_equal_to return x! = y;
greater return x > y;
less return x < y;
greater_equal return x > = y;
less_equal return x <= y;
logical_and return x && y;
logical_or return x
negate return - x;
logical_not return ! x;

例如,如果要求两个 double 型变量 x、y 的乘积,可以写:

multiplies<double> () (x, y)

less 是 STL 中最常用的函数对象类模板,其定义如下:

template <class_Tp>
struct less
{bool operator() (const_Tp & __x, const_Tp & __y) const{ return __x < __y; }
};

要判断两个 int 变量 x、y 中 x 是否比 y 小,可以写:

if( less<int>()(x, y) ) { ... }

引入函数对象后 STL 中的“大”、“小”和“相等”概念

默认情况下,STL 中的容器和算法比较元素的大小是通过<运算符进行的。sort 和 list::sort 都可以通过一个函数对象或函数自定义比较元素大小的规则。例如以下的 sort 版本:

template <class_RandIt, class Pred>
void sort(_RandIt first, _RandIt last, Pred op);

实际调用 sort 时,和 op 对应的实参可以是一个函数对象或者函数的名字。sort 在执行过程中用 op(x, y) 比较 x 和 y 的大小,因此可以将 op 称为自定义的“比较器”。

关联容器中的元素是从小到大排序的。使用关联容器时,也可以用自定义的比较器取代<运算符,以规定元素之间的大小关系。STL 中还有许多算法都可以自定义比较器。在自定义比较器 op 的情况下,以下三种说法是等价的:

	x 小于 y。op(x, y) 的返回值为 true。y 大于 x。

同样地,对关联容器的 find 和 count 成员函数以及其他一些在有序区间上的 STL 算法而言,在自定义比较器 op 的情况下,x和y相等与op(x, y)和op(y, x)都为假是等价的。

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

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

相关文章

sencha app watch php,我的第一个基于SenchaTouch的WebApp

经过进一周的各种折腾&#xff0c;各种想放弃&#xff0c;各种纠结&#xff0c;最终还是顺利的完成了SenchaTouch的开发&#xff0c;回想起来感觉“甜甜的”&#xff0c;也充分体会到Sencha MVC开发模式的好处&#xff0c;以及SenchaTouch.js和Extjs的强大和牛逼&#xff0c;不…

C++关联容器,STL关联容器

关联容器内部的元素都是排好序的&#xff0c;有以下四种。 set&#xff1a;排好序的集合&#xff0c;不允许有相同元素。multiset&#xff1a;排好序的集合&#xff0c;允许有相同元素。map&#xff1a;每个元素都分为关键字和值两部分&#xff0c;容器中的元素是按关键字排序的…

linux练习 串口跟进程6,linux下串口测试程序

linux下串口测试程序通过简单的参数配置&#xff0c;执行文件串口号波特率#include stdio.h#include stdlib.h#include unistd.h#include sys/types.h#include sys/stat.h#include fcntl.h#include errno.h#include sys/time.h#include time.h#include string.h#include sys/io…

C++ pair类模板

在学习关联容器之前&#xff0c;首先要了解 STL 中的 pair 类模板&#xff0c;因为关联容器的一些成员函数的返回值是 pair 对象&#xff0c;而且 map 和 multimap 容器中的元素都是 pair 对象。pair 的定义如下&#xff1a; template <class_Tl, class_T2> struct pair…

kali linux关闭进程,技术|如何使用 Kali Linux 黑掉 Windows

Kali Linux 派生自 Debian Linux&#xff0c;主要用于渗透测试&#xff0c;拥有超过 300 个的预安装好的渗透测试工具。Metasploit 项目中 Metasploit 框架支持 Kali Linux 平台&#xff0c;Metasploit 是一个用于开发和执行安全利用代码(security exploit)的工具。让我们来使用…

C++ multiset

multiset 是关联容器的一种&#xff0c;是排序好的集合&#xff08;元素已经进行了排序&#xff09;&#xff0c;并且允许有相同的元素。 不能直接修改 multiset 容器中元素的值。因为元素被修改后&#xff0c;容器并不会自动重新调整顺序&#xff0c;于是容器的有序性就会被破…

linux命令中的cp,Linux高级技术:关于cp命令中拷贝所有的写法

Linux高级技术&#xff1a;关于cp命令中拷贝所有的写法发布时间:2007-09-30 21:39:36来源:红联作者:GiftPot今天在编写一个脚本的时候&#xff0c;发现一个比较奇怪的问题&#xff1a;就是在使用cp拷贝当前目录下所有文件到目标目录的时候&#xff0c;源和目标目录大小不同。原…

C++ set

set 是关联容器的一种&#xff0c;是排序好的集合&#xff08;元素已经进行了排序&#xff09;。set 和 multiset 类似&#xff0c;它和 multiset 的差别在于 set 中不能有重复的元素。multiset 的成员函数 set 中也都有。 不能直接修改 set 容器中元素的值。因为元素被修改后…

linux内核实现ipsec,IP XFRM配置示例:利用linux kernel自带的IPSec实现,手动配置IPSec...

1、拓扑192.168.18.101 <> 192.168.18.1022、配置192.168.18.101ip xfrm state add src 192.168.18.101 dst 192.168.18.102 proto esp spi 0x00000301 mode tunnel auth md5 0x96358c90783bbfa3d7b196ceabe0536b enc des3_ede 0xf6ddb555acfd9d77b03ea3843f2653255afe8e…

linux 内核 82540网卡,Linux网卡as4.2 编译安装及配置准备

Linux网卡as4.2 编译安装及配置准备[日期&#xff1a;2008-03-28]来源&#xff1a;Linux公社作者&#xff1a;Linux整理[字体&#xff1a;大 中 小]确定make gcc kernel-devel包必须安装,没安装的话需要手动安装查看是否安装方法rpm -aq|gccrpm -aq|makerpm -aq|kernel-devel解…

C++ 容器适配器

STL 中的容器适配器有 stack、queue、priority_queue 三种。它们都是在顺序容器的基础上实现的&#xff0c;屏蔽了顺序容器的一部分功能&#xff0c;突出或增加了另外一些功能。 容器适配器都有以下三个成员函数&#xff1a; push&#xff1a;添加一个元素。top&#xff1a;返…

linux管理进程和计划任务,Linux进程和计划任务管理

本章结构#查看进程#控制进程#at一次性任务设置#crontab周期性任务设置程序和进程的关系#程序保存在硬盘、光盘等介质中的可执行代码和数据静态保存的代码#进程在cpu及内存中运行的程序代码动态执行的代码父、子进程&#xff1a;每个进程可以创建一个或多个进程查看进程ps#ps命令…

C++ stack

stack 是容器适配器的一种。要使用 stack&#xff0c;必须包含头文件 。 stack就是“栈”。栈是一种后进先出的元素序列&#xff0c;访问和删除都只能对栈顶的元素&#xff08;即最后一个被加入栈的元素&#xff09;进行&#xff0c;并且元素也只能被添加到栈顶。栈内的元素不…

C++ queue和priority_queue

queue 和 priority_queue 都是容器适配器&#xff0c;要使用它们&#xff0c;必须包含头文件 。 queue queue 就是“队列”。队列是先进先出的&#xff0c;和排队类似。队头的访问和删除操作只能在队头进行&#xff0c;添加操作只能在队尾进行。不能访问队列中间的元素。 qu…

less linux命令,less 命令用法详解

less 在 Linux 中用来查看文件&#xff0c;它可以以分页的方式显示文件内容。目前和tail 是用来查看文件的常用命令命令格式less [参数] 文件1、使用示例less a.txt用来查看文件按f 或 space 向下翻一页按b向前翻一页其实很好记f即forward 向前b即backward 向后关键最强大的一点…

STL算法分类

STL 中&#xff0c;算法就是函数模板。STL 中的算法大多数是用来对容器进行操作的&#xff0c;如排序、 查找等。大部分算法都是在头文件 中定义的&#xff0c;还有些算法用于数值处理&#xff0c;定义在头文件 中。 STL 中的算法的分类方法。 1 . 不变序列算法。 2 . 变值算法…

linux系统时间代表,Linux上有两种时间,一种是硬件时间,一种是系统时间

date 系统时间clock/hwclock 硬件时钟cal 日历date打印或设置系统日期或时间&#xff0c;以给定格式显示或设置系统时间%a显示周%A 完整显示周%b显示月份%B 完整显示月%c e.g.,Thu Mar 3 23:05:25 2005%C 显示世纪%d显示几日%D …

linux下软件包清理,Linux运维知识:如何清理Linux系统中的孤立、无用的软件包

本文主要向大家介绍了Linux运维知识的如何清理Linux系统中的孤立、无用的软件包&#xff0c;通过具体的内容向大家展现&#xff0c;希望对的大家学习Linux运维知识有所帮助。在Linux下安装软件&#xff0c;通常会自动安装一些依赖包或库。在你卸载某个软件后&#xff0c;这个软…

linux 多个定时器,timer: 一个Linux下的超级精简的多重定时器:可实现成千上万个定时任务,定时精度可达纳秒级别,且同一时间点可添加不同的任务!适用于云后台服务和嵌入式等各种环境。...

MT_Timer(MT译为Multiple或Multi)一、介绍一个Linux下的超级简洁的定时器&#xff1a;利用epoll机制和timerfd新特性实现的多重、多用、多个定时任务实现。只需要使用TIMER_CREATE()接口创建一个定时器实体&#xff0c;即可向其添加成千上万个定时任务&#xff0c;定时任务可达…

C++ bitset类

bitset 模板类由若干个位&#xff08;bit&#xff09;组成&#xff0c;它提供一些成员函数&#xff0c;使程序员不必通过位运算就能很方便地访问、修改其中的任意一位。bitset 模板类在头文件 中定义如下&#xff1a; template <size_t N> class bitset {... };size_t 可…