使用高级语言编写程序的缺点是,越抽象,代码的效率越低。
例如下面的例子,我们将一个由小到大的存储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_function
和std::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算法的参数,提供了包括效率在内的各种优势,会更加稳定可靠。