当前位置: 首页 > article >正文

C++11(下)

C++11(下)

  • 1.条件变量
  • 2.包装器(重要)
  • 3.bind

🌟🌟hello,各位读者大大们你们好呀🌟🌟
🚀🚀系列专栏:【C++的学习】
📝📝本篇内容:条件变量;包装器;bind
⬆⬆⬆⬆上一篇:C++11(中)
💖💖作者简介:轩情吖,请多多指教(> •̀֊•́ ) ̖́-

1.条件变量

条件变量我们常常用于多线程中,来保证线程的同步异步
☞ 条件变量文档
在这里插入图片描述
我们来使用一下部分函数,我写了一个简单的生产者消费者模型代码,可以参考一下

#include <iostream>
#include <thread>
#include <condition_variable>
#include <stack>
#include <mutex>
#include <cstdlib>
#include <ctime>
#include <Windows.h>
using namespace std;
condition_variable cv_productor;//定义个条件变量,生产者的
condition_variable cv_customer;//定义个条件变量,消费者的
stack<int> container;//存放数据的容器
mutex mtx;
#define SIZE 5
void Productor()
{
	while (1)
	{
			unique_lock<mutex> ck(mtx);
			while (container.size() == SIZE)//如果满足这个条件,线程就会挂起等待
			{
				cv_productor.wait(ck);//挂起后等待唤醒
			}
			container.push(rand() % 100 + 1);
			cv_customer.notify_one();//唤醒消费者一个线程
	
		//Sleep(1000);
	}
}

void Customer()
{
	while (1)
	{
		unique_lock<mutex> ck(mtx);
		while (container.empty())
		{
			cv_customer.wait(ck);
		}
		int ret = container.top();
		container.pop();
		cout << ret << endl;
		fflush(stdout);
		cv_productor.notify_one();//唤醒生产者其中一个线程
		Sleep(1000);//让消费者慢一点
	}
}

int main()
{
	srand((unsigned)time(nullptr));
	thread thread_array_productor[SIZE];
	for (int i = 0; i < SIZE; i++)//生产者
	{
		thread_array_productor[i] = thread(Productor);//创建线程并启动
	}

	thread thread_array_customer[SIZE];
	for (int i = 0; i < SIZE; i++)//消费者
	{
		thread_array_customer[i] = thread(Customer);//创建线程并启动
	}
	


	//主线程等待回收线程
	for (auto& e : thread_array_productor)
	{
		e.join();
	}

	for (auto& e : thread_array_customer)
	{
		e.join();
	}

	return 0;
}

如果对生产消费模型不太理解的,可以看我的另一篇博客☞Linux多线程

2.包装器(重要)

接下来要讲的包装器也是很重要的,在C++11当中是非常夺目耀眼的
Function包装器也叫做适配器,本质上就是一个类模板☞function包装器文档
为什么要使用它呢?
看下面这个例子

#include <functional>
#include <iostream>
using namespace std;
struct Add
{
	int operator()(int x,int y)
	{
		return x + y;
	}
};

int add(int x, int y)
{
	return x + y;
}

template<class T>
void Func(T way)
{
	cout<<way(4,2)<<endl;
}


int main()
{
	//1.使用函数对象
	Func(Add());
	//2.使用函数指针
	Func(add);
	//3.使用lambda
	Func([](int x, int y) {return x + y; });

	return 0;
}

虽然能够正常执行,但是其他效率低下,Func模板生成了三份实例,因此我们的function提供了统一的类型

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
//模板参数说明:
//Ret: 被调用函数的返回类型
//Args…:被调用函数的形参

使用优化后的代码,如下

#include <functional>
#include <iostream>
using namespace std;
struct Add
{
	int operator()(int x,int y)
	{
		return x + y;
	}
};

int add(int x, int y)
{
	return x + y;
}

template<class T>
void Func(T way)
{
	cout<<way(4,2)<<endl;
}


int main()
{
	//1.使用函数对象
	function<int(int, int)> f1 = Add();
	Func(f1);
	//2.使用函数指针
	function<int(int, int)> f2 = add;//function有移动构造
 	Func(f2);
	//3.使用lambda
	function<int(int,int)> f3 = [](int x, int y) {return x + y; };
	Func(f3);

	return 0;
}

接下来讲一下对于类的成员函数怎么使用function包装器

#include <iostream>
#include <functional>
using namespace std;
typedef int T;
class Calculator
{
public:
	static T Add(T x,T y)
	{
		return x + y;
	}

	T Sub(T x, T y)
	{
		return x - y;
	}
};

int main()
{
	//对于类成员函数如何使用function如下

	//处理静态函数
	//任选其一
	function<int(int, int)> f1 = Calculator::Add;
	function<int(int, int)> f2 = &Calculator::Add;
	cout << f1(1, 2) << endl;
	cout << f2(1, 2) << endl;

	//处理类成员函数	
	function<int(Calculator,int, int)> f3 =&Calculator::Sub;//必须加上&
	cout<<f3(Calculator(), 1, 2)<<endl;
	Calculator c;
	cout << f3(c,1, 2) << endl;

	//模板类型使用指针,但是不能传输右值对象了,因为右值无法取地址
	function<int(Calculator*, int, int)> f4 = &Calculator::Sub;//必须加上&
	cout << f4(&c, 1, 2);


	return 0;
}

其实function包装器底层也是调用的是operator()方法,对于类的成员函数,function的模板类型用与不用指针的区别不过是调用的时候用不用指针的问题罢了
我们再看一下是否能够修改调用参数的值

#include <iostream>
#include <functional>
using namespace std;
typedef int T;
class Calculator
{
public:
	static T Add(T x,T y)
	{
		return x + y;
	}

	T Sub(T x, T y)
	{
		_val = 10;
		return x - y;
	}

	void Try(Calculator& cal)
	{
		cal._val = 9;
	}

	int _val=0;//增加一个成员变量,来看function包装器会不会修改对象的值
};

int main()
{
	//处理类成员函数	
	function<int(Calculator,int, int)> f3 =&Calculator::Sub;//必须加上&
	Calculator c;
	f3(c, 1, 2);
	cout << c._val << endl;

	//模板类型使用指针,但是不能传输右值对象了,因为右值无法取地址
	function<int(Calculator*, int, int)> f4 = &Calculator::Sub;//必须加上&
	f4(&c, 1, 2);//使用指针就修改了
	cout << c._val << endl;

	function<void(Calculator, Calculator&)> f5 = &Calculator::Try;
	f5(c,c);
	cout << c._val << endl;

	return 0;
}

在这里插入图片描述

3.bind

bind是一个函数模板,就像一个函数包装器,接收一个可以调用的对象,生成一个新的可调用对象来适应原对象的参数列表
调用bind的一般形式:auto newCallable=bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数
arg_list中可能包含形如_n的名字,其中n是整数,这些参数是“占位符”,表示newCallable的参数,他们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数位置,_1为第一个参数,_2为第二个参数,以此类推

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

☞bind文档
使用见下面的代码,bind主要用在类的成员函数多一点

#include <functional>
#include <iostream>
using namespace std;
typedef int T;
class Calculator
{
public:
	static T Add(T x, T y)
	{
		return x + y;
	}

	T Sub(T x, T y)
	{
		_val = 10;
		return x - y;
	}

	void Try(Calculator& cal)
	{
		cal._val = 9;
	}

	int _val = 0;//增加一个成员变量,来看function包装器会不会修改对象的值
};


int main()
{
	//绑定成员函数
	
	//单纯的绑定this
	//&是必须的,placeholders是std命名空间里的,_1,_2是placeholders命名空间里的
	function<int(int,int)> f1 = bind(&Calculator::Sub, Calculator(), placeholders::_1, placeholders::_2);
	cout << f1(19, 31) << endl;//不再需要对象或对象指针
	
	//绑定全部参数
	function<int()> f2 = bind(&Calculator::Sub, Calculator(),19,31);
	cout << f2() << endl;

	//bind还有调整参数位置的作用
	Calculator c;
	//使用_1,_2改变参数位置
	//绑定为类对象
	function<int(int,int)> f3 = bind(&Calculator::Sub,c, placeholders::_2, placeholders::_1);
	cout << f3(19, 31) << endl;//实际应该为31-19

	//绑定为类指针
	function<int(int, int)> f4 = bind(&Calculator::Sub,&c, placeholders::_2, placeholders::_1);
	cout << f4(19, 31) << endl;//实际应该为31-19
	return 0;
}

在这里插入图片描述

我们来看一下,绑定的值是否会改变

#include <functional>
#include <iostream>
using namespace std;
typedef int T;
class Calculator
{
public:
	static T Add(T x, T y)
	{
		return x + y;
	}

	T Sub(T x, T y)
	{
		_val = 10;
		return x - y;
	}

	void Try(Calculator& cal)
	{
		cal._val = 9;
	}

	int _val = 0;//增加一个成员变量,来看function包装器会不会修改对象的值
};


int main()
{

	Calculator c;
	//使用_1,_2改变参数位置
	//绑定为类对象
	function<int(int, int)> f3 = bind(&Calculator::Sub, c, placeholders::_2, placeholders::_1);
	//cout << f3(19, 31) << endl;//实际应该为31-19
	cout << c._val << endl;//不会改变

	//绑定为类指针
	function<int(int, int)> f4 = bind(&Calculator::Sub, &c, placeholders::_2, placeholders::_1);
	//cout << f4(19, 31) << endl;//实际应该为31-19
	cout << c._val << endl;//不会改变
	
	//绑定参数
	function<void()> f5 = bind(&Calculator::Try, &c,c);
	f5();
	cout << c._val<< endl;//不会改变

	c._val = 0;
	//不绑定参数
	function<void(Calculator&)> f6 = bind(&Calculator::Try, &c, placeholders::_1);
	f6(c);
	cout << c._val << endl;
	return 0;
}

在这里插入图片描述
可以发现,绑定注的成员无法改变,但是不绑定的就能改变

🌸🌸C++11(下)的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪


http://www.kler.cn/a/419279.html

相关文章:

  • Uniapp 使用自定义字体
  • Graphy 是一款终极、易于使用、功能齐全的 FPS 计数器、统计监视器和调试器,适用于您的 Unity 项目。
  • 本地学习axios源码-如何在本地打印axios里面的信息
  • Matlab mex- setup报错—错误使用 mex,未检测到支持的编译器...
  • [Linux] 进程间通信——匿名管道命名管道
  • 【Electron学习笔记(三)】Electron的主进程和渲染进程
  • Pinia管理用户数据
  • 【Unity-摩擦力】
  • 【K8s】【部署】集群部署
  • 4.3_CMS漏洞
  • 区块链学习笔记(2)--区块链的交易模型part1
  • 每日速记10道java面试题04
  • transformer学习笔记-词嵌入embedding原理
  • Y20030012基于php+mysql的药店药品信息管理系统的设计与实现 源码 配置 文档
  • ECharts柱状图-极坐标系下的堆叠柱状图,附视频讲解与代码下载
  • 基于Java实现的潜艇大战游戏
  • 数据集搜集器(百科)008
  • 容器化与 Kubernetes:现代应用的编排与管理
  • LwIP协议栈 基础知识介绍
  • 电商项目高级篇06-缓存
  • 前端将echarts的图和element表格 一起导出到excel中
  • el-tree的使用及控制全选、反选、获取选中
  • 韩顺平 一周学会Linux | Linux 实操篇-组管理和权限管理
  • 根据后台数据结构,构建搜索目录树
  • openssl 基本命令使用方法
  • Oracle之提高PLSQL的执行性能