STL源码剖析之仿函数

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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值