函數對象
文章目錄
<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()
速度慢,更靈活。