Skip to content

Latest commit

 

History

History
332 lines (276 loc) · 7.92 KB

s3.md

File metadata and controls

332 lines (276 loc) · 7.92 KB

C++的函数式编程

  1. C++ STL中的绑定器
    bind1st : operator()的第一个形参变量绑定成一个确定的值 bind2nd : operator()的第二个形参变量绑定成一个确定的值
  2. (C++11从Boost库中引入了)bind绑定器和function函数对象机制
  3. lambda表达式 底层依赖函数对象的机制实现的

bind绑定器-bind1st和bind2nd

  
#include <iostream>
#include <vector> 
#include <functional>
#include <algorithm>
#include <ctime>
using namespace std;

template<typename Container>
void showContainer(Container &con)
{
	// Container没有实例化需要在前面加上typename实例化=>告知编译器Container::iterator是个类型
	typename Container::iterator it = con.begin(); 
	for (; it != con.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> 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<int>()); // 大到小排序
	showContainer(vec);

	/* 
	做一个功能:
	把70按顺序插入到vec容器当中 => 找第一个小于70的数字
	operator()(const T &val) <= 只需要接收一个参数,STL库里的是二元函数对象
	greater   a > b
	less      a < b
	绑定器 + 二元函数对象 => 一元函数对象
	bind1st: + greater bool operator()(70, const _Ty& _Right) => 使用sort中的greater
	bind2nd: + less bool operator()(const _Ty& _Left, 70)	  => 使用sort中的less,绑定第二个参数
	*/
	auto it1 = find_if(vec.begin(), vec.end(),
		bind1st(greater<int>(), 70));
		
	/*
	auto it1 = find_if(vec.begin(), vec.end(),
		bind2nd(less<int>(), 70));
	*/
	if (it1 != vec.end())
	{
		vec.insert(it1, 70);
	}
	showContainer(vec);

	return 0;
}

注意到下面代码实现了把70按顺序插入到vec容器当中。

auto it1 = find_if(vec.begin(), vec.end(),
	bind1st(greater<int>(), 70));

源码文件里还有对其底层实现的讲解,就不赘述了。(我也还不太懂)

注: bind1st和bind2nd已经废弃了,只能作为学习

function的使用

C++11提供的绑定器和函数对象 bind function C++ STL bind1st和bind2nd => 本身还是一个函数对象 function : 绑定器,函数对象,lambda表达式 => 它们只能使用在一条语句中(局限) 当在多条语句中应用时,需要function => 留下绑定器,函数对象,lambda表达式这些的类型

function和C语言的函数指针类似,但是其功能更丰富可以和类更深度的结合。

  
#include <iostream>
#include <vector> 
#include <map>
#include <functional>  // 使用function函数对象类型
#include <algorithm>
#include <ctime>
#include <string>
using namespace std;

void hello1()
{
	cout << "hello world!" << endl;
}
void hello2(string str) // void (*pfunc)(string)
{
	cout << str << endl;
}
int sum(int a, int b)
{
	return a + b;
}

class Test
{
public: // 成员方法调用必须依赖一个对象void (Test::*pfunc)(string)
	void hello(string str) { cout << str << endl; }
};

int main()
{
	/*
	1.用函数类型实例化function
	2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数
	*/
	// 从function的类模板定义处,看到希望用一个函数类型实例化function
	function<void()> func1 = hello1; // 返回值是void,()代表没有参数
	func1(); // func1.operator()() => hello1()

	function<void(string)> func2 = hello2; // 返回值是void,(string)代表函数有一个string类型的参数
	func2("hello hello2!"); // func2.operator()(string str) => hello2(str)

	function<int(int, int)> func3 = sum;
	cout<<func3(20, 30)<<endl;

	// operator()
	function<int(int, int)> func4 = [](int a, int b)->int {return a + b; }; // 参数是(int a, int b),->int代表返回值是int型
	cout << func4(100, 200) << endl;
	// function成员方法调用需要依赖对象Test*相当于类的this指针
	function<void(Test*, string)> func5 = &Test::hello;
	func5(&Test(), "call Test::hello!");

	return 0;
}

bind绑定器

bind绑定器实现了将一个或多个参数绑定到函数对象(function).

说白了就是给调用函数对象并对其参数赋值。

总共有三种使用方式:

  1. 直接调用
#include <iostream>
#include <typeinfo>
#include <string>
#include <functional>
using namespace std;
using namespace placeholders; // bind占位符参数的作用域

void hello(string str) { cout << str << endl; }
int main()
{
	bind(hello, "hello bind!")();
}

上述代码输出hello bind!

  1. bind + 占位符实现带参数调用的bind

在上面的代码的main函数中新增如下代码

	bind(hello, placeholders::_1)("hello bind 2!");

注意此时的bind是可以待参数的,参数传入到占位符中,如果要对第二个参数赋值则是使用占位符placeholders::_2,以此类推。

此时代码输出"hello bind 2!"

  1. 使用function来复用bind

上面的代码还缺少复用bind的能力,在上面main函数中新增代码如下:

	function<void(string)> func1 = bind(hello, _1);
	func1("hello func!");
	std::string tmp = "hello str!";
	func1(tmp);

代码输出:

hello func!
hello str!

下面是一个更完整的用例。

#include <iostream>
#include <typeinfo>
#include <string>
#include <functional>
using namespace std;
using namespace placeholders; // bind占位符参数的作用域
/*
C++11 bind绑定器 => 返回的结果还是一个函数对象
*/


void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }
class Test
{
public:
	int sum(int a, int b) { return a + b; }
};
int main()
{
	// bind是函数模板 可以自动推演模板类型参数
	// bind调用时需要绑定所有参数
	bind(hello, "hello bind!")();
	cout << bind(sum, 10, 20)() << endl;
	cout << bind(&Test::sum, Test(), 20, 30)() << endl; // 调用类里的函数需要对象

	// bind + 参数占位符
	bind(hello, _1)("hello bind 2!");
	cout << bind(sum, _1, _2)(200, 300) << endl;
	// 上面存在一个问题:bind出了语句无法使用,没办法复用

	// function + 参数占位符 解决 => 绑定器出了语句,无法继续使用
	// 此处把bind返回的绑定器binder就复用起来了
	function<void(string)> func1 = bind(hello, _1);
	func1("hello china!");
	func1("hello shan xi!");
	func1("hello si chuan!");

	return 0;
}

实战:利用function和bind实现一个线程池

#include <iostream>
#include <typeinfo>
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include <thread>
using namespace std;
using namespace placeholders;
/*
muduo源码文件 threadpool.cc  thread.cc  bind function
*/
// 线程类
class Thread
{
public:
	Thread(function<void(int)> func, int no) :_func(func), _no(no) {} // 用函数对象类型来接受绑定器类型
	thread start() // 创建线程
	{
		thread t(_func, _no); // _func(_no)
		return t;
	}
private:
	function<void(int)> _func;
	int _no;
};

// 线程池类
class ThreadPool
{
public:
	ThreadPool() {}
	~ThreadPool()
	{
		// 释放Thread对象占用的堆资源
		for (int i = 0; i < _pool.size(); ++i)
		{
			delete _pool[i];
		}
	}
	// 开启线程池
	void startPool(int size)
	{
		for (int i = 0; i < size; ++i)
		{
			_pool.push_back(
				new Thread(bind(&ThreadPool::runInThread, this, _1), i)); // bind返回的是函数对象,i通过_1占位符(Thread类里的_no)传入
		}

		for (int i = 0; i < size; ++i)
		{
			_handler.push_back(_pool[i]->start()); // 调用每一个线程对象的start方法
		}

		for (thread &t : _handler)
		{
			t.join(); // 等待所有子线程运行完成
		}
	}
private:
	vector<Thread*> _pool; // 线程池
	vector<thread> _handler; // 线程实例(句柄)

	// 把runInThread这个成员方法充当线程函数  thread   pthread_create
	void runInThread(int id)
	{
		cout << "call runInThread! id:" << id << endl;
	}
};

int main()
{
	ThreadPool pool;
	pool.startPool(10);
	return 0;
}