作用:修饰函数对象/仿函数,提供适配功能,向仿函数/函数对象中传入额外参数。
问题案例:
使用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
using namespace std;
#include
#include //使用for_each遍历算法
#include //使用内建函数对象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
。
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_function
、operator()函数
的参数位置及参数类型相对应。
②参数绑定后,仿函数的类需以public方式继承类模板binary_function<形参类型1, 形参类型2, 返回值类型>
。
例:class Printer : public binary_function
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
using namespace std;
#include
#include //使用for_each遍历算法
#include //使用内建函数对象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
using namespace std;
#include
#include //使用for_each遍历算法
#include //使用内建函数对象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
问题案例:
使用find_if算法
和函数对象/仿函数
遍历容器的元素,查找容器中大于给定值的第1个元素。
函数原型:iterator find_if(iterator begin, const iterator end, _Pr _Pred);
需求:不修改operator()
函数体内容时,实现查找容器中小于给定值的第1个元素。
实现代码:查找vector容器中第1个大于8的元素
#include
using namespace std;
#include
#include //使用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;
}
解决方案1:使用一元取反适配器not1()
,对已有函数对象/仿函数取反。
步骤:
①使用一元取反适配器not1()
对已有函数对象/仿函数取反。
例:vector
注:一元取反适配器
not1()
属于内建函数对象,使用时需包含#include
。
②一元取反后,仿函数的类需以public方式继承类模板unary_function<形参类型, 返回值类型>
。
例:class GreaterThanEight : public unary_function
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
using namespace std;
#include
#include //使用find_if算法
#include //使用一元取反适配器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:使用一元取反适配器not1()
、内建关系仿函数bool greater
和函数对象适配器bind2nd()
。
步骤:
①使用函数对象适配器bind2nd()
,对关系仿函数bool greater
和给定值参数进行参数绑定。
例:bind2nd(greater
注:函数对象适配器
bind2nd()
、关系仿函数bool greater
属于内建函数对象,使用时需包含#include
。
②使用一元取反适配器not1()
,对已参数绑定的整个表达式取反。
例:not1(bind2nd(greater
参考代码2:使用一元取反适配器not1()
、关系仿函数bool greater
和函数对象适配器bind2nd()
。
#include
using namespace std;
#include
#include //使用find_if算法
#include //使用一元取反适配器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::iterator pos = find_if(v.begin(), v.end(), not1(GreaterThanEight()));
//使用一元取反适配器not1()、关系仿函数bool greater和函数对象适配器bind2nd()
/* 1.使用函数对象适配器bind2nd(),对关系仿函数bool greater和给定值参数进行参数绑定 */
/* 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;
}
问题案例:
使用sort算法
对vector容器的元素进行排序,默认为升序排序,即底层源码默认使用关系仿函数less
。
注:STL中
sort算法
的源码实现为sort(v.begin(), v.end(), less
,实现升序排序。());
若需实现降序排序,可采用以下两种等价方法:
①直接使用关系仿函数greater
,即() sort(v.begin(), v.end(), greater
。());
②利用二元取反适配器not2()
,即sort(v.begin(), v.end(), not2(less
。()));
需求:对vector容器的元素进行降序排序。
解决方案:使用二元取反适配器not2()
对内建关系仿函数bool less
进行取反。
实现代码:
#include
using namespace std;
#include
#include //使用for_each算法、sort算法
#include //使用二元取反适配器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取反 */
sort(v.begin(), v.end(), not2(less<int>()));
//sort(v.begin(), v.end(), greater()); //等价形式
for_each(v.begin(), v.end(), [](int val) {cout << val << " "; }); //9 7 6 3 1
cout << endl;
return 0;
}
作用:使用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
using namespace std;
#include
#include //使用for_each函数
#include //使用内建函数对象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
作用:使用mem_fun_ref(..)
或mem_fun(..)
对类的成员函数进行适配,实现成员函数的回调处理。
语法:
当容器中存储对象的实体时:mem_fun_ref(&类名::成员函数名)
当容器中存储对象的指针时:mem_fun(&类名::成员函数名)
for_each()
函数的作用:
①遍历容器中的元素;
②对容器中的元素进行统一的逻辑处理。
实现代码:
#include
using namespace std;
#include
#include //使用for_each算法
#include //使用内建函数对象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();
}