1.把该操作设计为一个函数,再将函数指针当成算法的一个参数;或者将该操作设计为一个所谓的仿函数(就语言而言其实是一个class),再以该仿函数产生一个对象,并将此对象作为算法的一个参数。
所以仿函数其实就是一个行为类似函数的对象。
仿函数类对象像函数一样被调用,调用仿函数类对象时,实际调用的是仿函数类中重载的operator()函数。
仿函数的主要用途是搭配STL算法。
template <class _ForwardIter1, class _ForwardIter2>
_ForwardIter1 search(_ForwardIter1 __first1, _ForwardIter1 __last1,
_ForwardIter2 __first2, _ForwardIter2 __last2)
template <class _ForwardIter1, class _ForwardIter2, class _BinaryPred>
_ForwardIter1 search(_ForwardIter1 __first1, _ForwardIter1 __last1,
_ForwardIter2 __first2, _ForwardIter2 __last2,
_BinaryPred __predicate)
指定比较方法版本的模板参数中多了一个谓词模板_BinaryPred,用于指定元素比较的准则。而默认版本采用=操作符判断是否相等。
指定操作有多种方法:
- 通常可以通过定义函数,并将函数指针作为参数传递给算法。
- 也可以定义类,并在类中重载operator()函数,使得该类成为一个仿函数类。
- 使用Lambda匿名函数(C++11起)。
- 通用多态函数封装器std::function(C++11起)。
//单独定义仿函数
template<class T>
struct myless {
bool operator()(const T &a, const T &b) const {
return a < b ? true: false ;
}
};
//单独使用仿函数
int main()
{
myless<int> myer;
cout << myer.operator()(1, 2) << endl;
cout << myer(1, 2) << endl;
cout << myless<int>()(1, 2) << endl;
return 0;
}
常见的仿函数
可配接的关键
unary_function
//一元函数的参数和返回值类型,通常被继承
可以作为一个一元函数对象的基类,它只定义了参数和返回值的类型,本身并不重载()操作符,这个任务应该交由派生类去完成
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
// unary_function example
#include <iostream> // std::cout, std::cin
#include <functional> // std::unary_function
struct IsOdd : public std::unary_function<int,bool> {
bool operator() (int number) {return (number%2!=0);}
};
int main () {
IsOdd IsOdd_object;
IsOdd::argument_type input;
IsOdd::result_type result;
std::cout << "Please enter a number: ";
std::cin >> input;
result = IsOdd_object (input);
std::cout << "Number " << input << " is " << (result?"odd":"even") << ".\n";
return 0;
}
class Widget38 {};
template<typename T> class BPFC;
template<typename T>
class BPFCImpl : public std::unary_function<T, void> {
private:
Widget38 w; // 原来BPFC中所有数据现在都放在这里
int x;
virtual ~BPFCImpl(); // 多态类需要虚析构函数
virtual void operator()(const T& val) const;
friend class BPFC<T>; // 允许BPFC访问内部数据
};
binary_function
//二元函数的参数和返回值类型,通常被继承
binary_function可以作为一个二元函数对象的基类,它只定义了参数和返回值的类型,本身并不重载()操作符,这个任务应该交由派生类去完成。
template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
// binary_function example
2 #include <iostream> // std::cout, std::cin
3 #include <functional> // std::binary_function
4
5 struct Compare : public std::binary_function<int,int,bool> {
6 bool operator() (int a, int b) {return (a==b);}
7 };
8
9 int main () {
10 Compare Compare_object;
11 Compare::first_argument_type input1;
12 Compare::second_argument_type input2;
13 Compare::result_type result;
14
15 std::cout << "Please enter first number: ";
16 std::cin >> input1;
17 std::cout << "Please enter second number: ";
18 std::cin >> input2;
19
20 result = Compare_object (input1,input2);
21
22 std::cout << "Numbers " << input1 << " and " << input2;
23 if (result)
24 std::cout << " are equal.\n";
25 else
26 std::cout << " are not equal.\n";
27
28 return 0;
29 }
仿函数应用于算法
//仿函数和算法一起使用
template<class T>
struct myless {
bool operator()(const T &a, const T &b) const {
return a < b ? true: false ;
}
};
bool myfunction (int i,int j) { return (i < j); }
int main () {
int myints[] = { 32,71,12,45,26,80,53,33 };
std::vector<int> myvector(myints, myints + 8);
std::vector<int> myvector1(myvector);
std::vector<int> myvector2(myvector);
std::vector<int> myvector3(myvector);
// 函数指针
std::sort(myvector1.begin(), myvector1.end(), myfunction);
// 防函数
std::sort(myvector2.begin(), myvector2.end(), myless<int>());
//Lambda匿名函数
std::sort(myvector3.begin(), myvector3.end(), [](int a, int b)->bool {return a < b;
});
//输出:(12 26 32 33 45 53 71 80)
}
仿函数适配器
对返回值进行逻辑否定:not1、not2
class Widget33 {
public:
bool isCertified() const { return true; }
};
// 如果*pWidget是一个未被验证的Widget33,则删除该指针,并把它置成空
void delAndNullifyUncertified(Widget33*& pWidget)
{
if (!pWidget->isCertified()) {
delete pWidget;
pWidget;
}
}
int test_item_33()
{
std::vector<Widget33*> v;
for (int i = 0; i < 5; ++i) v.push_back(new Widget33);
// 删除那些指向未被验证过的Widget33对象的指针,会资源泄露
v.erase(std::remove_if(v.begin(), v.end(), std::not1(std::mem_fun(&Widget33::isCertified))), v.end());
// 一种可以消除资源泄露的做法
// 将所有指向未被验证的Widget33对象的指针删除并置成空
std::for_each(v.begin(), v.end(), delAndNullifyUncertified);
// 删除v中的空指针,必须将0转换成一个指针,这样C++才能正确推断出remove的第三个参数类型
v.erase(std::remove(v.begin(), v.end(), static_cast<Widget33*>(0)), v.end());
// 使用智能指针可防止资源泄露
std::vector<std::shared_ptr<Widget33>> v2;
for (int i = 0; i < 5; ++i) v2.push_back(std::make_shared<Widget33>());
// 下面语句需要编译器必须能够把智能指针类型std::shared<Widget33>隐式转换为对应的内置指针类型Widget33*才能通过编译
//v2.erase(std::remove_if(v2.begin(), v2.end(), std::not1(std::mem_fun(&Widget33::isCertified))), v2.end());
return 0;
}
bind1st、 bind2nd
bind1st(const Operation& op, const T& x)
bind2nd(const Operation& op, const T& x)
bind1st函数代表这么一个操作: x op value; bind2nd函数代表:value op x。
其中,value 是被应用bind函数的对象。这两个适配器函数都用于将一个二元算子转换成一个一元算子。
我们想知道一个vector中元素值大于100的元素个数,利用bind1st函数和less<int>标准库对象类,就是使得100<element。
#include "stdafx.h"
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> vec;
int a;
while (cin >> a)
vec.push_back(a);
cout << count_if(vec.begin(), vec.end(), bind1st(less<int>(), 100));//100 < element?
cout << endl;
return 0;
}
如果我们要知道容器中小于100的元素的个数
bind2nd(less<int>(),100) //element<100?
或者
bind1st(greater<int>(),100) //100>element?
ptr_fun
ptr_fun是将一个普通的函数适配成一个仿函数.
int LESS(int arg1, int arg2) {
if (arg1 < arg2)
return 1;
else
return 0;
}
int main()
{
vector<int> v{ 1,8,6,9,3 ,10,11 };
//绑定的是第二个参数,所以结果就是<7,即找比7小的元素个数
auto n = count_if(v.begin(), v.end(), bind2nd(ptr_fun(LESS), 7));
//绑定的是第一个参数,所以结果就是7<,即找比7大的元素个数
auto m = count_if(v.begin(), v.end(), bind1st(ptr_fun(LESS), 7));
cout << n << endl;
cout << m << endl;
system("pause");
return 0;
}
mem_fun及mem_fun_ref
将类的成员函数包装成仿函数使用
mem_fun()是一个适配器(adapter),该函数能将类的成员函数包装成仿函数使用,于是成员函数可以搭配各种泛型算法完成所谓的多态调用。
当容器中存放的是对象实体的时候用mem_fun_ref,当容器中存放的是对象的指针的时候用mem_fun。
class Widget33 {
public:
bool m_isc{ true };
bool isCertified() { return m_isc; }
};
int test_item_33()
{
std::vector<Widget33*> v;
for (int i = 0; i < 5; ++i) v.push_back(new Widget33);
v[2]->m_isc = false;
for_each(v.begin(), v.end(), std::mem_fun(&Widget33::isCertified));
//
std::vector<Widget33> vv(4);
vv[2].m_isc = false;
for_each(vv.begin(), vv.end(), std::mem_fun_ref(&Widget33::isCertified));
vv.erase(std::remove_if(vv.begin(), vv.end(), std::mem_fun_ref(&Widget33::isCertified)),vv.end());
return 0;
}