C++ STL(十一):适配器(函数对象适配器、取反适配器、函数指针适配器、成员函数适配器)


1 函数对象适配器【bind2nd()、bind1st()】

作用:修饰函数对象/仿函数,提供适配功能,向仿函数/函数对象中传入额外参数

问题案例
使用for_each算法函数对象/仿函数遍历容器的元素时:
①若希望原容器的元素经某种运算(如与给定值 100进行加法运算)后,再输出运算结果;
给定值可作为operator()函数的参数传递(如可由用户键盘输入,而并非在operator()函数体内直接使用固定值100);
for_each(iterator begin, iterator end, _Fn _Func)函数只支持3个参数,且第3个参数需传入函数对象/仿函数,对于给定值参数无额外的参数位置。
函数原型_Fn for_each(_InIt _First, _InIt _Last, _Fn _Func);

#include <iostream>
using namespace std;
#include <vector>

#include <algorithm>	//使用for_each遍历算法
#include <functional>	//使用内建函数对象bind2nd()

class Printer {
    
    
public:
	//重载函数调用运算符()
	void operator()(int val) {
    
    
		//需求1:原容器元素经某种运算(如加100)后,再输出运算结果。
		//需求2:给定值可作为operator()函数的参数传入。
		cout << val + 100 << endl;	//缺点:在operator()函数体内直接使用固定值100
	}
};

int main() {
    
    
	vector<int> v;

	//向vector添加元素
	for (int i = 0; i < 5; i++) {
    
    
		v.push_back(i);
	}

	//遍历vector容器:for_each算法 + 匿名函数对象
	for_each(v.begin(), v.end(), Printer());	

	return 0;
}

解决方案
使用函数对象适配器,在for_each()函数的第3个参数位置,将函数对象/仿函数给定值参数等两个参数绑定为一个参数

步骤
①在for_each()函数中,使用内建函数对象bind2nd()bind1st(),将仿函数给定值参数等两个参数绑定为一个参数。
例:for_each(v.begin(), v.end(), bind2nd(Printer(), given));

注:使用内建函数对象bind2nd()时,需包含头文件#include <functional>

bind2nd():将给定值given绑定至operator()函数的第2个参数位置
函数原型bind2nd<_Fn, T>(const _Fn& _Func, const T& _Right);
bind1st():将给定值given绑定至operator()函数的第1个参数位置
函数原型bind1st<_Fn, T>(const _Fn& _Func, const T& _Left);

注:bind2nd()bind1st()参数绑定的位置不同,使用时需保证与类模板binary_functionoperator()函数参数位置参数类型相对应。

参数绑定后,仿函数的类需以public方式继承类模板binary_function<形参类型1, 形参类型2, 返回值类型>
例:class Printer : public binary_function<int, double, void>{..}

operator()函数为二元(2个参数)时,继承二元函数的父类binary_function(_Arg1 arg1, _Arg2 arg2, _Result res)
operator()函数为一元(1个参数)时,继承一元函数的父类unary_function(_Arg arg, _Result res)

类模板定义
binary_function(二元函数的基类)

template <class _Arg1, class _Arg2, class _Result>
/* 二元函数的基类 */
struct binary_function {
    
     // base class for binary functions
    using first_argument_type  = _Arg1;
    using second_argument_type = _Arg2;
    using result_type          = _Result;
};

unary_function(一元函数的基类)

template <class _Arg, class _Result>
/* 一元函数的基类 */
struct unary_function {
    
     // base class for unary functions
    using argument_type = _Arg;
    using result_type   = _Result;
};

重写父类的operator()函数,并添加const关键字修饰为常函数
例:void operator()(int val, double given) const {..}


参考代码1:使用bind2nd()绑定仿函数和给定值

#include <iostream>
using namespace std;
#include <vector>

#include <algorithm>	//使用for_each遍历算法
#include <functional>	//使用内建函数对象bind2nd()

/* 2.参数绑定后,仿函数的类需以public方式继承类模板binary_function<形参类型1, 形参类型2, 返回值类型> */
class Printer : public binary_function<int, double, void> {
    
    
public:
	/* 3.重写父类的operator()函数,并添加const关键字修饰为常函数 */
	void operator()(int val, double given) const {
    
    
		cout << "val = " << val 
			<< " , given = " << given
			<< " , result = " << val + given << endl;
	}
};

int main() {
    
    
	vector<int> v;
	//向vector添加元素
	for (int i = 0; i < 5; i++) {
    
    
		v.push_back(i);
	}

	//给定值given可由用户由键盘输入
	cout << "请输入给定值given:" << endl;
	double given;
	cin >> given;

	//遍历vector容器:for_each算法 + 匿名函数对象
	/* 1.在for_each()函数中,使用内建函数对象bind2nd(),将仿函数与给定值参数进行绑定 */
	for_each(v.begin(), v.end(), bind2nd(Printer(), given));

	return 0;
}
/* 输出结果 */
请输入给定值given:
6.66
val = 0 , given = 6.66 , result = 6.66
val = 1 , given = 6.66 , result = 7.66
val = 2 , given = 6.66 , result = 8.66
val = 3 , given = 6.66 , result = 9.66
val = 4 , given = 6.66 , result = 10.66

参考代码2:使用bind1st()绑定仿函数和给定值

#include <iostream>
using namespace std;
#include <vector>

#include <algorithm>	//使用for_each遍历算法
#include <functional>	//使用内建函数对象bind2nd()

/* 2.参数绑定后,仿函数的类需以public方式继承类模板binary_function<形参类型1, 形参类型2, 返回值类型> */
class Printer : public binary_function<double, int, void> {
    
    
public:
	/* 3.重写父类的operator()函数,并添加const关键字修饰为常函数 */
	void operator()(double given, int val) const {
    
    
		cout << "val = " << val 
			<< " , given =" << given
			<< " , result = " << val + given << endl;
	}
};

int main() {
    
    
	vector<int> v;
	//向vector添加元素
	for (int i = 0; i < 5; i++) {
    
    
		v.push_back(i);
	}

	//给定值given可由用户由键盘输入
	cout << "请输入给定值given:" << endl;
	double given;
	cin >> given;

	//遍历vector容器:for_each算法 + 匿名函数对象
	/* 1.在for_each()函数中,使用内建函数对象bind1st(),将仿函数与给定值参数进行绑定 */
	for_each(v.begin(), v.end(), bind1st(Printer(), given));

	return 0;
}
/* 输出结果 */
请输入给定值given:
6.66
val = 0 , given = 6.66 , result = 6.66
val = 1 , given = 6.66 , result = 7.66
val = 2 , given = 6.66 , result = 8.66
val = 3 , given = 6.66 , result = 9.66
val = 4 , given = 6.66 , result = 10.66

2 取反适配器【not1()、not2()】

2.1 一元取反适配器【not1()】

问题案例
使用find_if算法函数对象/仿函数遍历容器的元素,查找容器中大于给定值的第1个元素。
函数原型iterator find_if(iterator begin, const iterator end, _Pr _Pred);
需求:不修改operator()函数体内容时,实现查找容器中小于给定值的第1个元素。

实现代码:查找vector容器中第1个大于8的元素

#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>	//使用find_if算法

//查找大于8的值
class GreaterThanEight {
    
    
public:
	//重载函数调用运算符()
	bool operator()(int val) {
    
    
		return val > 8;
	}
};

void func1() {
    
    
	vector<int> v;
	v.push_back(9);
	v.push_back(1);
	v.push_back(7);
	v.push_back(6);
	v.push_back(3);

	//_InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred);
	//iterator find_if(iterator begin, const iterator end, _Pr _Pred);
	vector<int>::iterator pos = find_if(v.begin(), v.end(), GreaterThanEight());

	if (pos != v.end()) {
    
    
		cout << "查找到第1个大于8的元素:" << *pos << endl;	//9
	}
	else {
    
    
		cout << "未查找到大于8的元素" << endl;
	}
}

int main() {
    
    
	func1();

	return 0;
}

2.1.1 对已有函数对象/仿函数的一元取反

解决方案1:使用一元取反适配器not1(),对已有函数对象/仿函数取反。
步骤
①使用一元取反适配器not1()对已有函数对象/仿函数取反。
例:vector<int>::iterator pos = find_if(v.begin(), v.end(), not1(GreaterThanEight()));

注:一元取反适配器not1()属于内建函数对象,使用时需包含#include <functional>

一元取反后,仿函数的类需以public方式继承类模板unary_function<形参类型, 返回值类型>
例:class GreaterThanEight : public unary_function<int, bool> {..}

operator()函数为二元(2个参数)时,继承二元函数的父类binary_function(_Arg1 arg1, _Arg2 arg2, _Result res)
operator()函数为一元(1个参数)时,继承一元函数的父类unary_function(_Arg arg, _Result res)

重写父类的operator()函数,并添加const关键字修饰为常函数
例:bool operator()(int val) const {..}

参考代码1:使用一元取反适配器not1(),对已有函数对象/仿函数取反。

#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>	//使用find_if算法
#include <functional>	//使用一元取反适配器not1()

//查找大于8的值
/* 2.一元取反后,仿函数的类需以public方式继承类模板unary_function<形参类型, 返回值类型> */
class GreaterThanEight : public unary_function<int, bool> {
    
    
public:
	/* 3.重写父类的operator()函数,并添加const关键字修饰为常函数。 */
	bool operator()(int val) const {
    
    
		return val > 8;
	}
};

//查找小于8的值-一元取反
void func2() {
    
    
	vector<int> v;
	v.push_back(9);
	v.push_back(1);
	v.push_back(7);
	v.push_back(6);
	v.push_back(3);

	/* 1.使用一元取反适配器`not1()`对已有函数对象/仿函数取反 */
	vector<int>::iterator pos = find_if(v.begin(), v.end(), not1(GreaterThanEight()));

	if (pos != v.end()) {
    
    
		cout << "查找到第1个小于8的元素:" << *pos << endl;	//1
	}
	else {
    
    
		cout << "未查找到小于8的元素" << endl;
	}
}

int main() {
    
    
	func2();
	return 0;
}

2.1.2 对内建关系仿函数的参数绑定及一元取反

解决方案2:使用一元取反适配器not1()内建关系仿函数bool greater<T>函数对象适配器bind2nd()
步骤
①使用函数对象适配器bind2nd(),对关系仿函数bool greater<T>给定值参数进行参数绑定
例:bind2nd(greater<int>(), 8)

注:函数对象适配器bind2nd()关系仿函数bool greater<T>属于内建函数对象,使用时需包含#include <functional>

②使用一元取反适配器not1(),对已参数绑定的整个表达式取反
例:not1(bind2nd(greater<int>(), 8))

参考代码2:使用一元取反适配器not1()关系仿函数bool greater<T>函数对象适配器bind2nd()

#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>	//使用find_if算法
#include <functional>	//使用一元取反适配器not1()

//查找小于8的值
void func3() {
    
    
	vector<int> v;
	v.push_back(9);
	v.push_back(1);
	v.push_back(7);
	v.push_back(6);
	v.push_back(3);

	//使用一元取反适配器not1()对已有函数对象/仿函数取反
	//vector<int>::iterator pos = find_if(v.begin(), v.end(), not1(GreaterThanEight()));

	//使用一元取反适配器not1()、关系仿函数bool greater<T>和函数对象适配器bind2nd()
	/* 1.使用函数对象适配器bind2nd(),对关系仿函数bool greater<T>和给定值参数进行参数绑定 */
	/* 2.使用一元取反适配器not1(),对已参数绑定的整个表达式取反 */
	vector<int>::iterator pos = find_if(v.begin(), v.end(), not1(bind2nd(greater<int>(), 8)));

	if (pos != v.end()) {
    
    
		cout << "查找到第1个小于8的元素:" << *pos << endl;	//1
	}
	else {
    
    
		cout << "未查找到小于8的元素" << endl;
	}
}

int main() {
    
    
	func3();
	return 0;
}

2.2 二元取反适配器【not2()】

问题案例
使用sort算法对vector容器的元素进行排序,默认为升序排序,即底层源码默认使用关系仿函数less<T>()

注:STL中sort算法的源码实现为sort(v.begin(), v.end(), less<int>());,实现升序排序
若需实现降序排序,可采用以下两种等价方法:
①直接使用关系仿函数greater<T>(),即sort(v.begin(), v.end(), greater<int>());
②利用二元取反适配器not2(),即sort(v.begin(), v.end(), not2(less<int>()));

需求:对vector容器的元素进行降序排序
解决方案:使用二元取反适配器not2()内建关系仿函数bool less<T>进行取反。
实现代码

#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>	//使用for_each算法、sort算法
#include <functional>	//使用二元取反适配器not2()

int main() {
    
    
	vector<int> v;
	v.push_back(9);
	v.push_back(1);
	v.push_back(7);
	v.push_back(6);
	v.push_back(3);

	//排序前
	for_each(v.begin(), v.end(), [](int val) {
    
    cout << val << " "; });	//9 1 7 6 3
	cout << endl;

	//sort算法-默认升序排序
	//sort(v.begin(), v.end());
	sort(v.begin(), v.end(), less<int>());		//源码默认实现

	//升序排序后(默认)
	for_each(v.begin(), v.end(), [](int val) {
    
    cout << val << " "; });	//1 3 6 7 9
	cout << endl;	

	//降序排序后
	/* 使用二元取反运算符not2(),对关系仿函数less<T>取反 */
	sort(v.begin(), v.end(), not2(less<int>()));
	//sort(v.begin(), v.end(), greater<int>());	//等价形式
	
	for_each(v.begin(), v.end(), [](int val) {
    
    cout << val << " "; });	//9 7 6 3 1
	cout << endl;

	return 0;
}

3 函数指针适配器【ptr_fun()】

作用:使用ptr_fun(..)普通函数或函数指针进行适配,允许类似仿函数/函数对象的使用方式。如实现普通回调函数给定值参数的绑定及回调处理,即向回调函数中传入额外参数
语法ptr_fun(函数名/函数指针名)

注:函数指针适配器ptr_fun()是STL底层源码实现中pointer_to_unary_function<_Arg, _Result, _Result(CALL_OPT*)(_Arg)>pointer_to_binary_function<_Arg1, _Arg2, _Result, _Result(CALL_OPT*)(_Arg1, _Arg2)>对外接口,分别继承自一元函数的基类unary_function<_Arg, _Result>二元函数的基类binary_function<_Arg1, _Arg2, _Result>


需求:(与函数对象适配器类似)
使用for_each算法回调函数遍历容器的元素时:
①若希望原容器的元素经某种运算(如与给定值 100进行加法运算)后,再输出运算结果;
给定值可作为回调函数的参数传递(如可由用户键盘输入,而并非在回调函数的函数体内直接使用固定值100);
for_each(iterator begin, iterator end, _Fn _Func)函数只支持3个参数,且第3个参数需传入普通回调函数,对于给定值参数无额外的参数位置。
函数原型_Fn for_each(_InIt _First, _InIt _Last, _Fn _Func);


解决方案
使用函数指针适配器,在for_each()函数的第3个参数位置,将回调函数给定值参数等两个参数绑定为一个参数
例:for_each(v.begin(), v.end(), bind2nd(ptr_fun(回调函数名), 给定参数));

注:普通(回调)函数不同于仿函数/函数对象,无法继承类,且无法使用const修饰

实现代码

#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>	//使用for_each函数
#include <functional>	//使用内建函数对象bind2nd()

//回调函数
void myPrint(int val, double given) {
    
    
	cout << "val = " << val
		<< " , given = " << given
		<< " , result = " << val + given << endl;
}

int main(){
    
    
	vector<int> v;

	//向vector添加元素
	for (int i = 0; i < 5; i++) {
    
    
		v.push_back(i);
	}

	//给定值given可由用户由键盘输入
	cout << "请输入给定值given:" << endl;
	double given;
	cin >> given;

	/* 函数指针适配器ptr_fun(),实现回调函数与给定值参数的绑定及回调处理 */
	for_each(v.begin(), v.end(), bind2nd(ptr_fun(myPrint), given));

	return 0;
}
/* 输出结果 */
请输入给定值given:
3.33
val = 0 , given = 3.33 , result = 3.33
val = 1 , given = 3.33 , result = 4.33
val = 2 , given = 3.33 , result = 5.33
val = 3 , given = 3.33 , result = 6.33
val = 4 , given = 3.33 , result = 7.33

4 成员函数适配器【mem_fun_ref()、mem_fun()】

作用:使用mem_fun_ref(..)mem_fun(..)类的成员函数进行适配,实现成员函数的回调处理
语法
当容器中存储对象的实体时:mem_fun_ref(&类名::成员函数名)
当容器中存储对象的指针时:mem_fun(&类名::成员函数名)

for_each()函数的作用
①遍历容器中的元素;
②对容器中的元素进行统一的逻辑处理

实现代码

#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>	//使用for_each算法
#include <functional>	//使用内建函数对象mem_fun_ref()或mem_fun()

class Person {
    
    
public:
	string name;
	int age;

	Person(string name, int age) {
    
    
		this->name = name;
		this->age = age;
	}

	//成员函数
	void showInfo() {
    
    
		cout << "姓名:" << this->name 
			<< ",年龄:" << this->age << endl;
	}

	//成员函数
	void addAge() {
    
    
		this->age++;
	}
};

//成员函数适配器-mem_fun_ref()适配对象实体
void func1() {
    
    
	//创建存储Person对象实体的vector容器
	vector<Person> v;
	Person p1("Tom", 16);
	Person p2("Jerry", 18);
	Person p3("Jack", 20);
	Person p4("Lucy", 22);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	/* 成员函数适配器mem_fun_ref(),调用类的成员函数 */
	for_each(v.begin(), v.end(), mem_fun_ref(&Person::showInfo));	//显示信息
	//姓名:Tom,年龄:16
	//姓名:Jerry,年龄:18
	//姓名:Jack,年龄:20
	//姓名:Lucy,年龄:22

	//for_each()函数的作用:1.遍历容器的元素;2.对容器的元素实现统一的逻辑处理
	for_each(v.begin(), v.end(), mem_fun_ref(&Person::addAge));		//更新成员属性

	for_each(v.begin(), v.end(), mem_fun_ref(&Person::showInfo));	//显示信息
	//姓名:Tom,年龄:17
	//姓名:Jerry,年龄:19
	//姓名:Jack,年龄:21
	//姓名:Lucy,年龄:23
}


//成员函数适配器-mem_fun()适配对象指针
void func2() {
    
    
	//创建存储Person对象指针的vector容器
	vector<Person *> v;
	Person p1("Tom", 16);
	Person p2("Jerry", 18);
	Person p3("Jack", 20);
	Person p4("Lucy", 22);

	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);

	/* 成员函数适配器mem_fun_ref(),调用类的成员函数 */
	for_each(v.begin(), v.end(), mem_fun(&Person::showInfo));	//显示信息
	//姓名:Tom,年龄:16
	//姓名:Jerry,年龄:18
	//姓名:Jack,年龄:20
	//姓名:Lucy,年龄:22

	//for_each()函数的作用:1.遍历容器的元素;2.对容器的元素实现统一的逻辑处理
	for_each(v.begin(), v.end(), mem_fun(&Person::addAge));		//更新成员属性

	for_each(v.begin(), v.end(), mem_fun(&Person::showInfo));	//显示信息
	//姓名:Tom,年龄:17
	//姓名:Jerry,年龄:19
	//姓名:Jack,年龄:21
	//姓名:Lucy,年龄:23
}

int main() {
    
    
	//func1();
	func2();
}

猜你喜欢

转载自blog.csdn.net/newson92/article/details/114156359