函数对象
文章目录
<algorithm>
是所有stl头文件中最大的一个。
<numeric>
很小,只有几个在序列上进行数学运算的模板函数。
<functional>
定义了一些模板类。
算法可以直接修改对象,也可以复制一份。
算法分类:
- 非可变序列算法:不直接修改容器内容。
-
- 计数:
count,count_if
- 搜索:
search,find,find_if,find_first_of
- 比较:
equal,mismatch,lexicographical_compare
- 计数:
- 可变序列算法:可修改容器内容。
-
- 删除:
remove,remove_if,remove_copy
- 修改:
for_each,transform
- 排序:
sort,stable_sort,partial_sort
- 删除:
函数对象:重载函数调用操作符的类,其对象称为函数对象。通过重载类的operator()
实现。
有时可以把它看作函数对待。
1. 函数对象做参数和返回值
template <class InputIterator, class Function> Function for_each (InputIterator first, InputIterator last, Function fn);
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<functional>
template <typename T>
class ShowElement{
public:
int a;
public:
ShowElement()
{
a = 0;
}
void operator()(T &t)
{
cout<<t<<" ";
a++;
}
};
int main(int argc, char *argv[])
{
vector<int> v;
ShowElement<int> showElement;
ShowElement<int> show2;
v.push_back(1);
v.push_back(3);
v.push_back(5);
for_each(v.begin(),v.end(),showElement);
cout<<showElement.a<<endl;
show2 = for_each(v.begin(),v.end(),showElement);
cout<<show2.a<<endl;
show2 = for_each(v.begin(),v.end(),ShowElement<int>());//匿名函数对象
cout<<show2.a<<endl;
cin.get();
return 0;
}
/*
1 3 5 0
1 3 5 3
1 3 5 3
*/
对于第一个for_each(v.begin(),v.end(),showElement)
,改变的a
属于形参,不影响实参的a
。也就是说,for_each()
的函数对象参数,采用值传递,而不是引用传递,并且返回函数对象。
stl算法返回值有两种:迭代器和函数对象.
函数对象相对于回调函数的好处是,可以保持调用状态信息,即使使用匿名函数对象。
2. 谓词
算法谓词,Predicate,即标准库算法传递的参数。
一元谓词,即一个参数。
谓词的返回值为bool
类型,以返回值的结果作为函数的操作条件。为此的形参要与对应容器的类型相同。
template <class InputIterator, class UnaryPredicate> InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);
第三个参数是一个谓词Predicate
,函数返回谓词为真的容器位置的迭代器。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
template <typename T>
class IsDiv
{
public:
T divisor;
public:
IsDiv(const T &divisor)
{
this->divisor = divisor;
}
bool operator()(T &t)
{
return (t % divisor == 0);
}
};
int main(int argc, char *argv[])
{
vector<int> v;
IsDiv<int> div(4);
for(int i = 10; i < 20; i++)
{
v.push_back(i);
}
vector<int>::iterator it;
it = find_if(v.begin(),v.end(),div);
if(it == v.end())
{
cout<<"no result."<<endl;
}
else
{
cout<<*it<<" is divisible by "<<div.divisor<<endl;
}
cin.get();
return 0;
}
/*
12 is divisible by 4
*/
二元
#include<iostream>
#include<vector>
using namespace std;
template <typename T>
class SumAdd
{
public:
T operator()(T t1,T t2)
{
return t1 + t2;
}
};
int main()
{
vector<int> v1,v2;
vector<int> v3;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v2.push_back(2);
v2.push_back(4);
v2.push_back(6);
v3.resize(10);
transform(v1.begin(),v1.end(),v2.begin(),v3.begin(),SumAdd<int>());
for(vector<int>::iterator it = v3.begin(); it != v3.end(); it++)
{
cout<<*it<<" ";
}
return 0;
}
/*
3 7 11 0 0 0 0 0 0 0
*/
二元谓词典型的用法是判断式,比如比较大小。
#include<iostream>
#include<vector>
using namespace std;
bool MyComp(const int&a,const int &b)
{
return a < b;
}
int main(int argc, char *argv[])
{
vector<int> v1;
for(int i = 0; i < 10; i++)
{
v1.push_back(rand()%100);
}
for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout<<*it<<" ";
}
cout<<endl;
sort(v1.begin(),v1.end(),MyComp);
for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout<<*it<<" ";
}
cout<<endl;
system("pause");
return 0;
}
二元谓词与set
:
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
struct CompNoCase
{
bool operator()(const string &str1, const string &str2) const //const!!!!!!!!!!!!!!!!!!!!!
{
string sTempStr1;
sTempStr1.resize(str1.size());
transform(str1.begin(), str1.end(), sTempStr1.begin(), tolower);
string sTempStr2;
sTempStr2.resize(str2.size());
transform(str2.begin(), str2.end(), sTempStr2.begin(), tolower);
return (sTempStr1 < sTempStr2);
}
};
int main(int argc, char *argv[])
{
set<string,CompNoCase> set1;
set1.insert("aaa");
set<string,CompNoCase>::iterator it = set1.find("aAa");
if (it == set1.end())
{
cout << "no result" << endl;
}
else
{
cout << "found" << endl;
}
system("pause");
return 0;
}
/*
found
*/
3. 预定义函数对象和函数适配器
不常用。
<functional>
有很多预定义的函数对象,比如plus<>
可对不同的数据类型进行加法运算。
#include<iostream>
#include<functional>
#include<string>
using namespace std;
int main(int argc, char *argv[])
{
plus<string> stringAdd;
string s1 = "aa";
string s2 = "bb";
cout << stringAdd(s1,s2) << endl;
system("pause");
return 0;
}
/*
aabb
*/
这样就通过函数对象技术实现了数据结构和算法的分离。
函数适配器
有4类:
- bind adapter
- composite adapter
- pointer function adapter
- member function adapter
前两种常用。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
using namespace std;
int main(int argc, char *argv[])
{
vector<string> v1;
v1.push_back("aaa");
v1.push_back("aaa");
v1.push_back("bbb");
string sa = "aaa";
cout << count_if(v1.begin(), v1.end(), bind2nd(equal_to<string>(),sa)) << endl;
system("pause");
return 0;
}
/*
2
*/
bind2nd()
函数适配器把预定义函数对象和一个参数进行了绑定。
其它函数适配器辅助函数:bind1st(),not1(),not2()
.
//calc the number of odd numbers
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
using namespace std;
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(6);
cout << count_if(v1.begin(), v1.end(), not1(bind2nd(modulus<int>(),2))) << endl;
system("pause");
return 0;
}
/*
3
*/
4. stl的容器算法迭代器的设计理念
容器通过类模板技术,实现数据类型和容器模型的分离;算法通过函数对象,实现自定义数据类型的算法运算。
函数对象的核心思想:回调函数。
5. 两个修改型算法
for_each()
用指定函数对指定范围内所有元素进行迭代访问,该函数不得修改序列中的元素。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
using namespace std;
class CMyShow
{
private:
int num;
public:
CMyShow()
{
num = 0;
}
void operator()(int &n)
{
num++;
cout << n << " ";
}
void printNum()
{
cout << "num:" << num << endl;
}
};
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
CMyShow m;
CMyShow m1 = for_each(v1.begin(), v1.end(), CMyShow());//init
m1.printNum();
m1 = for_each(v1.begin(), v1.end(), m); //assign
m1.printNum();
system("pause");
return 0;
}
/*
1 2 3 num:3
1 2 3 num:3
*/
transform()
与for_each()
类似,但可修改容器元素。
当源与目标相同时,则和for_each()
一样。
两种形式:
OutputIterator transform (InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op);
OutputIterator transform (InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, OutputIterator result,BinaryOperation binary_op);
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
#include<list>
#include<iterator>
using namespace std;
int increase(int i)
{
return i + 1;
}
void printV(vector<int> v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
//callback function
transform(v1.begin(),v1.end(),v1.begin(),increase);
printV(v1);
//defined function object
transform(v1.begin(), v1.end(), v1.begin(), negate<int>());
printV(v1);
// function adapter
list<int> l;
l.resize(v1.size());
transform(v1.begin(),v1.end(),l.begin(),bind2nd(multiplies<int>(),10));
list<int>::iterator it = l.begin();
while (it != l.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//output directly
transform(v1.begin(), v1.end(), ostream_iterator<int>(cout," "), negate<int>());
system("pause");
return 0;
}
/*
2 3 4
-2 -3 -4
-20 -30 -40
2 3 4
*/
两者比较
两者对函数对象的要求不同。
for_each()
的函数对象,参数是引用,可以没有返回值,transform()
的函数对象,参数不是引用,必须有返回值。可调试分析源码。
for_each()
速度快,不灵活;transform()
速度慢,更灵活。