4.3.1 函数对象的回调行为
通过上面的讲解,已经实现了自己的函数对象,那么函数对象到底是什么呢?
首先函数对象是一个类对象,它是一个重载了函数调用操作符的类所定义的对象。这个类中必须包含函数调用操作符()的重载,其次它可以有自己的成员属性和成员方法,比如,上面我们定义的类中除了有函数调用操作符的重载,还有私有属性m_count和成员方法get_count()。
在for_each中对函数对象的使用上来看,我们不难发现,函数对象的行为很像一个函数,确切的说很像一个回调函数(C语言中函数指针做函数参数),所以它也叫仿函数。下面我们用一个普通函数来做for_each的参数,测试我们的猜想。
首先,定义一个模板函数
template<typename T>
void my_add(T& t)
{
t++;
}
我们把这个函数做参数,并在main函数中继续添加如下测试代码
for_each(V.begin(), V.end(), my_add<int>);
print_vector_int(V);
编译运行,查看打印结果
可以看到,通过普通的模板函数,我们也实现了函数对象的功能,对容器中每个元素遍历并加1。由此可见,函数对象确实和回调函数有着类似的功能。
4.3.2 函数对象和回调函数的区别
既然回调函数和函数对象可以实现相同的功能,那么为什么要用函数对象呢?
首先,我们知道,函数对象是一个重载了()操作符的类所定义的对象,既然是类对象,那么他便可以拥有自己的属性和方法。就像前面我们通过类的私有属性m_count来计数容器中的元素个数,并通过成员函数get_count()来返回私有属性m_count。类中可以封装属性和方法,通过类对象做函数参数可以把属性和方法一块传入调用函数中,这是普通函数所不具备的。
4.4 完整代码
最后附上完整代码
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
void print_vector_int(vector<int>& v)
{
//for(vector<int>::iterator it = v.begin(); it != v.end(); it++)
//{
// cout << *it << " ";
//}
for (unsigned int i = 0; i < v.size(); i++)
{
cout << v.at(i) << " ";
}
cout << endl;
}
template<typename _MyType>
class MySort
{
public:
MySort()
{
this->m_count = 0;
}
public:
void operator()(_MyType& t) //怎么确定这个函数对象的参数呢?
{
t++;
this->m_count++;
}
public:
int get_count()
{
return m_count;
}
private:
int m_count; //记录容器中元素个数
};
template<typename T>
void my_add(T& t)
{
t++;
}
int main()
{
vector<int> V;
for (int i = 0; i < 10; i++)
{
V.push_back(rand());
}
print_vector_int(V);
/*
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); //范围:容器迭代器 begin 到 end
auto _UFirst = _Get_unwrapped(_First); //指针_UFirst指向迭代器_First的位置
const auto _ULast = _Get_unwrapped(_Last); //指针_ULast迭代器_Last的位置
for (; _UFirst != _ULast; ++_UFirst) { //遍历容器
_Func(*_UFirst); //把容器元素逐个放入函数_Func作为其函数参数
}
return _Func; //返回函数对象
}
*/
for_each(V.begin(), V.end(), MySort<int>()); //匿名对象
print_vector_int(V);
MySort<int> m_sort1; //MySort有私有变量m_count,所以需要提供构造函数初始化m_count
for_each(V.begin(), V.end(), m_sort1);
cout << "计数:" << m_sort1.get_count() << endl;
print_vector_int(V);
MySort<int> m_sort2; //MySort有私有变量m_count,所以需要提供构造函数初始化m_count
m_sort2 = for_each(V.begin(), V.end(), m_sort2);
cout << "计数:" << m_sort2.get_count() << endl;
print_vector_int(V);
for_each(V.begin(), V.end(), my_add<int>);
print_vector_int(V);
system("pause");
return 0;
}
总结
学习STL最好的教材就是源码,通过分析源码可以深刻理解STL容器、迭代器和算法的精髓。参考源码,才能写出我们自己的、编译器认可的、规范的代码。