stl使用中的经验(十八)--使用函数对象作为算法的参数

使用高级语言编写程序的缺点是,越抽象,代码的效率越低。

例如下面的例子,我们将一个由小到大的存储10000000个double类型的vector调用sort算法排序,并且让他的顺序改为由大到小。

typedef vector<double> DVec;
typedef vector<double>::iterator DVecIter;

DVec dv;
clock_t t1 = clock();
cout << "time 1 " << t1 << endl;
for(int index = 1; index <= 10000000; ++index)
{
    dv.push_back(0.3456 * index + 122);
}

clock_t t2 = clock();
cout << "time 2 " << t2 - t1 << endl;

那么在stl算法中,最常见的方式就是直接调用sort算法,并且传入一个类型为greater<double>的函数对象。

sort(dv.begin(), dv.end(), greater<double>());

但是前面我们说了,在使用高级语言编写程序的时候,越抽象,效率越低。所以我们就有了另一种选择。

inline bool doubleGreater(const double d1, const double d2)
{
	return d1 < d2;
}

sort(dv.begin(), dv.end(), doubleGreater);

为了避免抽象带来的影响,我们手写函数,并且为了更好的效率,将其声明为内联函数。

但是,如果你真的运行过上面这两种不同的调用形式后,你就会发现,其实,在使用greater<double>函数对象的运行效率会快很多。这与我们前面的越抽象,效率越低的说法是有很大区别的。

而对上面的这种行为的解释也是非常简单的:函数内联。所谓函数内联,就是如果一个函数对象的operator()函数已经被声明为内联函数(直接被显示的声明,或者通过定义在类内部的隐式声明);那么他的函数体则直接被编译器使用。

我们再来看一下greater<double>::operator()的函数定义。

template <class _Tp>
struct greater : public binary_function<_Tp,_Tp,bool> 
{
  bool operator()(const _Tp& __x, const _Tp& __y) const
  { 
       return __x > __y;
  }
};

greater<double>::operator()是一个内联函数,所以编译器在实例化sort的过程中直接将其展开。所以,在调用sort的过程中其实是没有函数调用的。因此编译器就可以对这段不包含函数调用的代码进行优化。

sort(dv.begin(), dv.end(), greater<double>());

而当我们将手写的函数doubleGreater传递给sort时,其实并不是直接的函数调用,因为在C/C++中,我们不能将一个函数做为参数传递给另一个函数。而是会被隐式的转换为一个函数指针

所以我们调用上面的手写函数的时候其实将一个doubleGreater函数的指针传递给了sort算法。编译器会将其展开为下面的函数声明。

void sort(vector<double>::iterator begin,
         vector<double>::iterator end,
         bool (*comp)(double, double)));

所以,sort内部在每次用到comp函数的时候。都会进行一次间接的函数调用。也就是通过指针发起的调用。

而大多数情况下,编译器是不会对函数指针参与的函数调用进行内联优化。也就说,函数指针的调用会抑制内联机制。

有了上面的参考,突然想起,我们前面已经说过的,若类是一个函数子,则应使它可配接,我们可以根据类的operator函数中参数的个数相应的从std::unary_functionstd::binary_function中选择继承来实现函数,因此也就有了下面的写法。

struct doubleGreat : public binary_function<const double, const double, bool>
{
	bool operator()(const double& d1, const double& d2) const
	{
		return d1 < d2;
	}
};

sort(dv.begin(), dv.end(), doubleGreat());

写完之后,和前面的greater<double>()的调用对比才发现,我们调用的是一样的,只不过是已经将模板类进行了实例化。那么,是不是这种调用应该和greater<double>()的效率大差不差呢?

编写测试用例试一下:

#include <iostream>
#include <vector> 
#include <algorithm> 
#include <iterator>
#include <ctime>
#include <windows.h>
using namespace std;

inline bool doubleGreater(const double d1, const double d2)
{
	return d1 < d2;
}

struct doubleGreat : public binary_function<const double, const double, bool>
{
	bool operator()(const double& d1, const double& d2) const
	{
		return d1 < d2;
	}
};

int main()
{
	typedef vector<double> DVec;
	typedef vector<double>::iterator DVecIter;

	DVec dv;
	clock_t t1 = clock();
	cout << "time 1 " << t1 << endl;
	for(int index = 1; index <= 10000000; ++index)
	{
		dv.push_back(0.3456 * index + 122);
	}
	
	clock_t t2 = clock();
	cout << "time 2 " << t2 - t1 << endl;
	
	sort(dv.begin(), dv.end(), greater<double>());
	
	clock_t t3 = clock();
	cout << "time 3 " << t3 - t2 << endl;
	
	sort(dv.begin(), dv.end(), doubleGreater);
	
	clock_t t4 = clock();
	cout << "time 4 " << t4 - t3 << endl;
	
	sort(dv.begin(), dv.end(), doubleGreat());
	
	clock_t t5 = clock();
	cout << "time 5 " << t5 - t4 << endl;
	
	return 0;
}

得到以下结果:

在这里插入图片描述

以函数对象做为STL算法的参数,提供了包括效率在内的各种优势,会更加稳定可靠。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值