std::thread_local 右值引用 shared_ptr __attribute__ ((__packed__)) 函数指针 std::function std::bind lambda表达

thread_local

该关键字修饰的变量具有线程周期属性,首先同static类似,都只初始化一次,但是thread_local是在某个线程的生命周期内只初始化一次,如果在函数中声明了该类型变量,当第二个线程执行该函数时,那么这个变量会第二次重新被初始化。

static void* func(void*){//类成员函数作为线程运行函数必须有static,普通函数无要求
  for(int i=0; i<5; i++){
  	thread_local int a = 0;
  	a++;
  	cout<<a<<" ";
  }
  return 0;
}
int main(){
pthread_t  t1;
//一个线程周期
pthread_create(&t1,nullptr,&func,nullptr);
pthread_join(t1,nullptr);//阻塞直到子线程运行完毕,回收子线程资源
//一个线程周期
pthread_create(&t1,nullptr,&func,nullptr);
pthread_join(t1,nullptr);
return 0;
}
//输出结果:1 2 3 4 5 1 2 3 4 5

右值引用 &&

当一个函数的参数为引用时,如果此时使用该函数,那么这个参数必须为左值,该参数在内存中需要有实际的地址,才能作为引用参数传入函数,当该值为一个右值时,那么引用就会出错,因为右值在内存中无实际的地址,此时就有右值引用。
使用:

void fun1(int& a){cout<<a<<endl;}
void fun2(int&& a){cout<<a<<endl;}
int main(){
	int a = 10;
	func1(10);//出错
	func1(a);//正确
	func2(10);//正确,右值引用
}

shared_ptr自定义释放规则

在初始化 shared_ptr 智能指针时,可以自定义所指堆内存的释放规则,这样当堆内存的引用计数为 0 时,会优先调用我们自定义的释放规则。
在某些场景中,自定义释放规则是很有必要的。比如,对于申请的动态数组来说,shared_ptr 指针默认的释放规则是不支持释放数组的,只能自定义对应的释放规则,才能正确地释放申请的堆内存。
对于申请的动态数组,释放规则可以使用 C++11 标准中提供的 default_delete 模板类,我们也可以自定义释放规则:

//指定 default_delete 作为释放规则
std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());

//自定义释放规则
void deleteInt(int*p) {
    delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);

//自定义释放规则可以采用lambda表达式
std::shared_ptr<int>  p7(new int[10], [](int* p) {delete[]p; });

自定义释放规则不一定用在释放内存上,也可以用于其他方面,比如文件的打开:

shared_ptr<FILE>  fp(fopen("./1.txt","r"), fclose);
size_t  len = fread(buff,1,10,fp.get());
//在智能指针作用域内读写一次文件智能指针就会关闭该文件,关闭文件的界限太模糊,不建议使用

attribute ((packed))

attribute ((packed))跟在结构体后面,目的是让结构体的成员以紧凑的方式进行内存布局,不会去进行内存对齐,可以用在一段二进制数据的序列化中,比如在buff中存储了int+double+int,那么就可以直接将这段buff memcpy到示例中的test2结构体中

struct  test1
{
	int  a;//4字节 + 自动对齐(+4字节)=8字节
	double  b;//8字节
	int  c;//4字节 + 自动对齐(+4字节)=8字节
};
struct  test2
{
	int  a;//4字节
	double  b;//8字节
	int  c;//4字节
}__attribute__((__packed__));
int len = sizeof(test1);//len = 24
int len = sizeof(test2);//len = 16
test2 t2;
char* buff[16];//假设里面已经存储了int+double+int的三个数据
memcpy(&t2,buff,sizeof(test2));//这样就可以直接反序列化出一段内存的数据,不需要使用指针去一个一个偏移取出

函数指针

用于指向一个函数的指针,其值为函数的入口地址

//定义一个函数指针(*func),该指针可以用于指向参数为(int, int)、返回值为int的函数
typedef int (*func)(int,int);
int add(int a,int b)
{
	return a+b;
}
int main()
{
	func f = add;
	int a = f(1,2);
	cout<<a<<endl;//输出3
	return 0;
}

std::function

std::function是一个函数包装器,可以用来包装任何可调用对象,包括普通函数,函数指针,静态成员函数,lambda表达式,仿函数等。std::function<>,<>中接收形如void(int)的参数,表示返回void,参数为一个int的函数,如std::function<int(void*,int>表示该function可以包装一个返回值为int,参数为void*和int的可调用对象。

//包装函数
void  func1(int  a){cout<<a<<endl;}
//包装静态成员函数
class  A{
public:
static  void  func2(int  a){cout<<a<<endl;}
};
//包装模板函数
template<typename  T>
void  func3(T  a){cout<<a<<endl;}
//包装函数指针
typedef  void (*fun_ptr)(int);
void  func4(int  a){cout<<a<<endl;}
//包装仿函数
struct  Func5{
void  operator()(int  a){cout<<a<<endl;}
};
//包装lambda表达式
auto  func6 = [](int  a){cout<<a<<endl;};
  
int  main()
{
	std::function<void(int)> callback;
	//全输出1
	callback  =  func1; callback(1);
	callback  =  A::func2; callback(1);
	callback  =  func3<int>; callback(1);
	fun_ptr  f_p= func4;
	callback  =  f_p; callback(1);
	Func5  f5;
	callback  =  f5; callback(1);
	callback  =  func6; callback(1);
}

std::bind

C++11中std::bind函数的意义就如字面上的意思一样,用来绑定函数调用的某些参数。std::bind的思想其实是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候再调用。而且这种绑定是非常灵活的,不论是普通函数还是函数对象还是成员函数都可以绑定,而且其参数可以支持占位符。
这里的std::placeholders::_1是一个占位符,且绑定第一个参数,若可调用实体有2个形参,那么绑定第二个参数的占位符是std::placeholders::_2,依次类推3 4,示例如下。

//包装类成员函数
class  C
{
public:
	void  func7(int  a){cout<<a<<endl;}
};
void output(int a,int b){cout<<a<<" "<<b<<endl;}
int  main()
{
	std::function<void(int)> callback;

	C  c;
	callback = std::bind(&C::func7,&c,std::placeholders::_1);
	callback(1);//输出1
	std::bind(output,1,2)(1,2);//输出1 2
	std::bind(output,1,2)(2,2);//输出1 2,第一个2被忽略
	//std::bind(output,1,std::placeholders::_2)(2);//出错,使用时传参缺少参数一
	std::bind(output,2,std::placeholders::_1)(1);//输出2 1
	std::bind(output,1,std::placeholders::_2)(1,2);//输出1 2
	std::bind(output,std::placeholders::_1,placeholders::_2)(1,2);//输出1 2
	std::bind(output,placeholders::_2,placeholders::_1)(1,2);//输出2 1
}

lambda表达式

就地匿名定义函数对象,优点 方便 简洁 好维护 功能闭包,缺点 调试麻烦
格式:
[捕获列表] (参数表) opt -> ret{ 函数体 };
opt是函数选项,一般可忽略,ret是函数体返回类型,lambda中可以由return推断出类型,故ret也可以省略。

捕获用法:
[ ] : 不捕获任何变量
[=]:按值捕获外部作用域的所有变量
[&]:按引用捕获外部作用域所有变量
int a,b;
[a]:只按值捕获a
[a,&b]:按值捕获a,按引用捕获b
int c;
[=,&c]:按值捕获所有变量,按引用捕获c
[this]:捕获this指针

//一个完整的lambda表达式,按值捕获外部变量n,函数参数为一个int类型,返回int类型
int n=1;
auto f = [n](int a)->int{return a+n;};
f(1);//返回2
//省略写法
auto f = [n](int a){return a+n;};
f(1);//返回2

//按值捕获的变量在经够lambda表达式后就已经被复制了一份,如下
int a = 0;
auto f = [=](){return a;};
a += 1;
f();//输出0
//如果想让lambda表达式中捕获的变量跟随外部一起变化,就需要按引用捕获
auto f = [&](){return a;};
a += 1;
f();//输出1

注意:按值捕获的变量不可以在lambda表达式内对其修改,非要修改就要声明为mutable

int a=0;
auto f = [=](){return ++a;};//error 值捕获不可修改
auto f = [&](){return ++a;};//OK
//声明为mutable也可以修改按值捕获的变量
//lambda表达式内的修改不会影响外部的变量a,因为a仍然是复制了一份
auto f = [=]() mutable {return ++a;};//OK

tuple

创建tuple和使用tuple

tuple<int,string> tp{1,"qiang"};
tuple<int,string> t = make_tuple(1,"qiang");//推荐使用
int  a = get<0>(tp); //1
string  b = get<1>(tp); // qiang
//也可以使用std::tie创建tuple和解tuple,用的少
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值