栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

117-C++11中引入的bind绑定器和function函数对象

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

117-C++11中引入的bind绑定器和function函数对象

1、C++11中引入的bind绑定器和function函数对象 1.1、bind1st和bind2d使用
  • bind1st和bind2d是STL中的;

  • bind1st和bind2d作用: 将二元函数对象的一个参数绑定,使其变为一元函数对象;

  • 缺点: 只能用于二元函数对象。

  • 函数对象: 对象拥有小括号重载函数的对象。

#include 		--包含c++库中的所有的函数对象
#include 		--包含了c++库中的所有的泛型算法

例1—改变排序顺序



greater是一个二元函数对象:(因为一次需要从容器中拿2个函数对象出来)


传入greater函数对象后,就是从大到小进行排序了。

#include 
#include  
#include 
#include 
#include 
using namespace std;

template
void showContainer(Container& con)
{
	typename Container::iterator it = con.begin();
	//编译器是从上到下编译的,这个还没有实例化,它不知道这个名字作用域后面的iterator是类型还是变量
	//typename告知编译器后面类型的作用域后面是类型
	for (; it != con.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main()
{
	vector vec;
	srand(time(nullptr));
	for (int i = 0; i < 20; ++i)
	{
		vec.push_back(rand() % 100 + 1);//随机出来的数字,并不是有序的 
	}

	showContainer(vec);
	sort(vec.begin(), vec.end());//默认小到大排序,传入的是起始和末尾的后继的迭代器
	showContainer(vec);
	//greater 二元函数对象
	sort(vec.begin(), vec.end(), greater());//大到小排序
	showContainer(vec);
	return 0;
}

例2—将70按顺序插入到vec容器中

问题:将70按顺序插入到vec容器中

使用find_if函数需要传入一个一元函数对象(一次从容器中拿出一个元素和70进行比较 )

注意:库里面提供的函数对象都是二元的,没有办法直接使用;怎么办?

使用绑定器; 将二元函数对象转换为一元函数对象;

使用greater函数对象,用绑定器将其绑定为一元函数对象:

bind1st:绑定第1个参数,也就是70 > b,b就是容器中的每个元素,找到第一个小于70的位置。

使用less函数对象,用绑定器将其绑定为一元函数对象:

bind2d:绑定第2个参数,a < 70,找到第一个小于70的位置。

注意,这是生成的随机数

1.2、bind1st和bind2d底层原理

实现自己的find_if—my_find_if

函数模板,接收用户传入的二元函数对象和所要绑定的值;

为什么用函数模板去封装?

不用自己去写模板参数,可以自己推导。

mybind1st就是将函数对象封装了一下。


测试:

直接使用自己的即可。

bind2d就是在类中重载时,将绑定的位置交换即可。

#include 
#include  
#include 
#include 
#include 
using namespace std;

template
void showContainer(Container& con)
{
	typename Container::iterator it = con.begin();
	for (; it != con.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
}

template
Iterator my_find_if(Iterator first, Iterator last, Compare comp)
//遍历这2个迭代器之间的元素,如果满足函数对象的运算,就返回当前迭代器,如果都不满足,返回end()
{
	for (; first != last; ++first)
	{
		if (comp(*first))//comp.operator()(*first)一元函数对象,因为要从容器拿1个元素和它指定的元素比较
		//my_find_if需要1元函数对象,而在库里面都是二元的
		{
			return first;
		}
	}
	return last;
}

template
class _mybind1st//绑定器是函数对象的一个应用
{
public:
	_mybind1st(Compare comp, T val)
		:_comp(comp), _val(val)
	{}
	bool operator()(const T& second)
	{
		return _comp(_val, second);//greater
	}
private:
	Compare _comp;
	T _val;
};

//mybind1st(greater(), 70)
template
_mybind1st mybind1st(Compare comp, const T& val)
{
	//直接使用函数模板,好处是,可以进行类型的推演
	return _mybind1st(comp, val);
}

int main()
{
	vector vec;
	srand(time(nullptr));
	for (int i = 0; i < 20; ++i)
	{
		vec.push_back(rand() % 100 + 1);
	}

	showContainer(vec);
	sort(vec.begin(), vec.end());//默认小到大排序
	showContainer(vec);

	//greater 二元函数对象
	sort(vec.begin(), vec.end(), greater());//大到小排序
	showContainer(vec);

	
	

	auto it1 = my_find_if(vec.begin(), vec.end(),
		mybind1st(greater(), 70));
	//auto it1 = my_find_if(vec.begin(), vec.end(),bind2nd(less(), 70));
	if (it1 != vec.end())
	{
		vec.insert(it1, 70);
	}
	showContainer(vec);

	return 0;
}
1.3、function函数对象类型的应用示例


function:函数对象类;

  • 绑定器、函数对象、lambda表达式实际上都是函数对象。
  • 如果我们最终得到的绑定器、函数对象、lambda表达式,这些函数对象,想在多条语句中应用 ,怎么办?如何将这些函数对象的类型留下来?


源码中希望你用一个函数类型来实例化function模板。

区别函数类型和函数指针类型:

函数指针类型:

  • 是一个指针类型,指向返回值是void,不带形参的函数。

函数类型:

  • 我们需要用函数类型来实例化function。
  • 只给出返回值和参数列表即可

    无参的hello1函数:


    用函数对象类型func1将hello1函数类型保留下来了,然后就可以使用func1()调用了。

测试结果:

对于有参的hello2函数:


运行结果:

sum函数:


运行结果:

上面说明,function非常好用,可以直接将函数类型留下来。

function不仅仅可以留下函数类型,也可以留下函数对象类型。(函数对象的本质是()重载函数)

也可以说function是对一个函数/函数对象的包装。

结果:
上面是对function作用于全局函数,将其函数类型留下来。

当然,function也可以将类的成员方法留下来!

成员方法的调用必须依赖一个对象。

在调用时,第一个参数对应传入一个临时对象即可。

小结:

function使用举例

funtion的作用:

  • 保留 全局函数、成员函数、其他函数对象的类型,然后再各处都可以使用。

    我们使用function,将函数的类型保存下来使用

问题:这里也可以用函数指针来接收函数的类型吗?

  • 可以接收函数类型;但是只能接收普通的函数,不能接收绑定器绑定的函数对象,也不能接收lambda表达式函数对象。

#include 
#include  
#include 
#include //使用function函数对象类型
#include 
#include 
#include 
using namespace std;


void doShowAllBooks() { cout << "查看所有书籍信息" << endl; }
void doBorrow() { cout << "借书" << endl; }
void doBack() { cout << "还书" << endl; }
void doQueryBooks() { cout << "查询书籍" << endl; }
void doLoginOut() { cout << "注销" << endl; }

int main()
{
	int choice = 0;

	map> actionMap;
	actionMap.insert({ 1, doShowAllBooks });//老版本:insert(make_pair(xx,xx));
	actionMap.insert({ 2, doBorrow });
	actionMap.insert({ 3, doBack });
	actionMap.insert({ 4, doQueryBooks });
	actionMap.insert({ 5, doLoginOut });

	for (;;)
	{
		cout << "-----------------" << endl;
		cout << "1.查看所有书籍信息" << endl;
		cout << "2.借书" << endl;
		cout << "3.还书" << endl;
		cout << "4.查询书籍" << endl;
		cout << "5.注销" << endl;
		cout << "-----------------" << endl;
		cout << "请选择:";
		cin >> choice;

		auto it = actionMap.find(choice);//map  pair  first second
		if (it == actionMap.end())
		{
			cout << "输入数字无效,重新选择!" << endl;
		}
		else
		{
			it->second();
		}
		//不好,因为这块代码无法闭合  无法做到“开-闭原则”
		
	}

	return 0;
}

1.4、模板的完全特例化和非完全特例化

有完全特例化,就选择对应的完全特例化,有部分特例化,就匹配部分特例化,没有的话,就原模板实例化

模板的完全特例化



能运用在int类型上,字符串比较就不行了。
此时的实例化compare代码就不行了,字符串比较的是地址,并不是字典的顺序。

需要多加一个模板的完全特例化:(完全特例化: 类型完全是已知的)

  • template后面的<>什么参数都没有,表示参数都是已知的。

    运行结果:
模板的部分特例化

原模板实例化:

完全特例化版本:

  • template后面的<>什么参数都没有,表示参数都是已知的。

    部分特例化版本:
  • vector后面知道它是一个指针类型,但不知道具体什么指针,template后面的<>指针类型。



    函数指针的部分特例化:

    函数类型的部分特例化:

区分函数类型和函数指针类型:

运行结果:

#include 
#include 
using namespace std;

template
class Vector
{
public:
	Vector() { cout << "call Vector template init" << endl; }
};

//下面这个是对char*类型提供的完全特例化版本  #1
template<>//特例化的语法
class Vector
{
public:
	Vector() { cout << "call Vector init" << endl; }
};

//下面这个是对指针类a型提供的部分特例化版本  #2
template
class Vector
{
public:
	Vector() { cout << "call Vector init" << endl; }
};

//指针函数指针(有返回值,有两个形参变量)提供的部分特例化
template
class Vector
{
public:
	Vector() { cout << "call Vector init" << endl; }
};

//针对函数(有一个返回值,有两个形参变量)类型提供的部分特例化
template
class Vector
{
public:
	Vector() { cout << "call Vector init" << endl; }
};

int sum(int a, int b) { return a + b; }

int main()
{
	Vector vec1;
	Vector vec2;
	Vector vec3;
	Vector vec4;
	Vector vec5;//function

	//注意区分一下函数类型和函数指针类型
	//用函数指针定义的变量本身就是一个指针
	typedef int(*PFUNC1)(int, int);		//PFUNC1是函数指针类型
	PFUNC1 pfunc1 = sum;
	cout << pfunc1(10, 20) << endl;

	typedef int PFUNC2(int, int);		//PFUNC2是函数指针
	//函数类型在定义的时候,需要将指针*加上
	PFUNC2* pfunc2 = sum;
	cout << (*pfunc2)(10, 20) << endl;

	return 0;
}

模板的实参推演




程序中添加一个sum函数;

程序推导出:

问题: 上面直接退的函数类型,范围太大了,有返回值类型,还有形参,我们想将返回值和所有形参的类型都取出来;

我们直接用一个函数指针的模板部分特例化来做:

我们就可以得到细分的类型。


定义一个Test类,里面有一个成员函数sum。



得到一个指向成员方法的函数指针类型。

我们将其参数细分,添加func3函数

我们直接用一个成员方法函数指针的模板部分特例化来做:


#include 
#include 
using namespace std;


int sum(int a, int b) { return a + b; }

//T包含了所有的大的类型  返回值,所有形参的类型都取出来
template
void func(T a)
{
	cout << typeid(T).name() << endl;
}

template
void func2(R(*a)(A1, A2))
{
	cout << typeid(R).name() << endl;
	cout << typeid(A1).name() << endl;
	cout << typeid(A2).name() << endl;
}

class Test
{
public:
	int sum(int a, int b) { return a + b; }
};

template
void func3(R(T::* a)(A1, A2))
{
	cout << typeid(R).name() << endl;
	cout << typeid(T).name() << endl;
	cout << typeid(A1).name() << endl;
	cout << typeid(A2).name() << endl;
}


int main()
{
	func(10);
	func("aaa");
	func(sum);//T  int (*)(int,int)   int (int,int)
	func2(sum);
	func(&Test::sum);//int (__thiscall Test::*)(int,int)
	func3(&Test::sum);

	return 0;
}

总结
  • 这几个例子指导我们在写函数模板,类模板时,针对一个具体情况,把他们一个大类型的细分类型,定义相应的参数,接收大类型的所有细分类型,使用的就是模板部分偏特化。
1.5、function函数实现原理
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/847105.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号