C++高阶之函数对象

定义

函数调用操作符

函数对象的定义跟函数的定义一样简单。首先,需要定义一个普通的类并定义它的函数调用操作符“operator()”。在函数调用操作符中,可以对数据进行处理,实现函数的所有功能。同时,因为类具有成员变量,可以将每次函数执行的状态数据保存到它的成员变量中,而在下次执行的时候可以访问这个成员变量从而得到上一次执行时的状态数据。

// 函数对象的类模板
template <typename T>
class mymax
{
	public:
	// 重载"()"操作符,在这个操作符中实现具体的函数功能
	// 这使得这个类的对象成为函数对象 
	T operator ()(T a, T b)
	{
		return a > b ? a : b;
	}
};

定义好函数对象类之后,就可以用它创建相应的函数对象,并利用这些函数对象来实现具体的功能。

// 创建函数对象
mymax<int> intmax;
int a = 0;
int b = 0;
cin>>a>>b; // 输入数据
// 使用函数对象比较 a 和 b 的大小,并返回较大值
int max = intmax(a,b);
// 输出比较结果
cout<<a<<"和"<<b<<"中较大的是"<<max<<endl;

首先用 int 实例化 mymax 类模板得到一个函数对象类mymax<int>,然后创建了一个 mymax<int>类的对象 intmax,因为这个类定义了“()”操作符,所以这个对象就是我们的函数对象。

上面对函数对象的调用实际上等同于:

// 调用函数对象的本质
int max = intmax.operator()(a,b);

函数对象是一个具体的实体对象,它既可以单独使用,也可以像函数指针一样被当成参数传递给其他函数,并在其他函数中使用。它一般没有构造函数和析构函数,因此,在创建或销毁函数对象的过程中不会有额外的性能消耗。同时, “( )” 操作符往往是一个内联函数,编译器能内联它的代码,从而避免了与函数调用相关的性能损失

利用函数对象记住状态数据

函数对象具有“记忆力”,它可以记住函数执行过程中的状态数据,从而使它可以应用在那些需要记住函数上次执行的状态数据的场景下。

在某些特殊情况下,下一次的函数执行是在上一次函数执行的结果基础上进行的。这时,函数就需要记住上一次的执行状态数据以备下一次函数执行使用。

例如,用一个函数统计容器中所有 Student 对象的身高,就需要在上次的统计结果的基础上进行累加,从而将所有身高都累加到一起。利用函数对象自身的成员变量,函数可以记住在每次执行过程中的状态数据,找回失去的记忆。

// 定义一个函数对象类
// 用于统计容器中所有 Student 对象的身高
class AverageHeight
{
	public:
		// 构造函数,对类的成员变量作合理的初始化
		AverageHeight() : m_nCount(0), m_nTotalHeight(0) {};
		// 定义函数调用操作符“()”,
		// 在其中完成统计的功能
		void operator () ( const Student& st )
		{
			// 将当前对象的身高累加到总身高中
			// 这里的 m_nTotalHeight 记录了上次累加的结果,
			// 这就是函数那失去的记忆
			m_nTotalHeight += st.GetHeight();
			// 增加已经统计过的 Student 对象的数目
			++m_nCount;
		}
		// 接口函数,获得所有统计过的 Student 对象的平均身高
		float GetAverageHeight()
		{
			if ( 0 != m_nCount )
				return (float)GetTotal()/GetCount();
		}
		// 获得函数对象类的各个成员变量
		int GetCount() const
		{
			return m_nCount;
		}
		int GetTotal() const
		{
			return m_nTotalHeight;
		}
	// 函数对象类的成员变量,
	// 用来保存函数执行过程中的状态数据
	private:
		int m_nCount; // 记录已经统计过的对象的数目
		int m_nTotalHeight; // 记录已经统计过的身高总和
};

现在有了这个可以找回记忆的函数对象类,就可以创建该类的函数对象,并将它应用到for_each()算法中来完成统计身高的任务了:

// …
// 创建函数对象
AverageHeight ah;
// 将函数对象应用到 for_each()算法中以完成统计
ah = for_each( vecStu.begin(), vecStu.end(), ah);
// 从函数对象中获取它的记忆作为结果输出
cout<<ah.GetCount()<<"个学生的平均身高是: "
<<ah.GetAverageHeight()<<endl;

首先创建了一个函数对象 ah并将它应用到 for_each()算法中,for_each()算法在执行的时候,会逐个地以容器中的 Student 对象作为实际参数来对这个函数对象的“( )”操作符进行调用。

这样,函数对象 ah 就有机会访问到容器中的每一个 Student 对象,自然也就可以把这些对象的身高累加到它自己的 m_nTotalHeight 成员变量上,同时它还会记录已经统计过的对象数目。

最后, for_each()算法会将完成统计后的函数对象作为结果返回,而这时的函数对象 ah 已经是一个保存了统计结果的函数对象了。通过函数对象提供的接口函数,可以轻松地获得统计结果并进行输出。

STL 中的函数对象

  • 算术操作
    这类函数对象类可以用于常见的算术运算,例如,加(plus)、减(minus)、乘(multiplies)、除(divides)、取余(modules)和取负(negate)等。
  • 比较操作
    这类函数对象类可以用于进行数据的比较,例如,等于( equal_to)、不等于(not_equal_to)、大于(greater)、小于(less)、大于等于(greater_equal)、小于等于(less_equal)。
  • 逻辑操作
    这类函数对象类可以用于逻辑运算,例如,逻辑与( logical_and)、逻辑或( logical_or)、 逻辑非(logical_not)。

STL 中的这些函数对象类都是一些类模板,在使用的时候,我们需要根据所处理的数据提供具体的类型参数。例如,我们需要将一个容器中的数据全部取负,就可以用 negate 函数对象类来完成:

vector<int> v = {54,65,-59,96,-61};
// 利用 negate 函数对象对数据取负
// 这里的“negate<int>()”得到的是一个临时的函数对象
transform(v.begin(),v.end(),v.begin(),negate<int>());
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值