一:stl遍历新方法
1.std::for_each遍历调用一个函数并传一个*it的用法
#include<algorithm>
std::for_each(.begin(),.end(),函数);
2.auto的简化新遍历用法
for(auto it =arr.begin();it!=arr.end();++it)
3.auto全范围新遍历用法
for(auto var:arr){}
for(auto& var:arr){}
for(const auto var:arr){}
for(const auto& var:arr){}
切记:该用法下所取的不是迭代器,而是容器里存的value_type,map对应pair;
4.对于取中间一段遍历,新用法没有简便办法,只能像2.简化,原始用法
二:lambda表达式,简称"中括号,小扩号,大扩号"
lambda表达式其实是一个末尾加了const修饰的函数
全语法: [变量捕获] (参数)opt->ret{函数体}
实际用法:[]()ret{}
作用域:当前作用域,类作用域,主要通过参数传值方式和作用变量体现
参数传递方式:引用,按值
作用变量:无变量即表示全部的意思
1.->可省略,不清楚是做什么用的
2.没有参数()可省略变成:[]ret{}
3.返回值无,或可以推导变成以下:[]{}
4.[]很重要,代表{}内使用的变量权限
<1>.2个作用域内默认捕获方式,若没指明,即不能使用外部变量,如[]
[&]:表达示中使用的全部外部变量按引用传递(默认捕获方式)
[=]:表达示中使用的全部外部变量按值传递(默认捕获方式)
<2>.自由组合捕获方式
[this]:此表达式视为该类的成员变量,可访问类的全部成员
[var]:表达示中使用的var按值传递,表达示中不使用除var的外部变量
[&var]:表达示中使用的var按引用传递,表达示中不使用除var的外部变量
以下可以任意组合:但是一个变量的权限不能与指明的默认捕获方式重复,表达式会内建函数把需要的变量丢进函数内,同个权限的变量不能出现两次
[=,a,b] :对
[this,a,b] :对
[this,&,b] : 对
[=,a,b] :错误 a,b本来就已是按值捕获
[=a] :错误 不能加=
[&,a,&b] :错误 b和前面冲突
意外:经实际程序验证,按值传递的变量,连修改变量的权限都没,和函数完全不一样,只能把值赋给函数中申请的变量再作修改
结论:lambda表达式其实是一个末尾加了const修饰的函数
auto f = [x,&xx](int var)
{
x+=5; //此步要报错,提示改成&%
x = 10;//此步要报错,提示改成&%
int t = x;
t++;
xx--;
return var + x + xx ;
};
5.opt
<1>.mutable
表达式选项目前仅遇到过这种,表达式内部的传值变量也可以修改了,突破const限制;
若有mutable修饰的表达式,()参数即使没参数也不能省
6.用法
<1>.直接可以把表达式赋给std::function
std::function<int(int(int)> f = [](int a){return a;};
f(123);
<2>.直接用于构造std::bind
std::function<int(int(int)> f = std::bind([](int a){return a;},123);
f(123);
<3>.把表达式直接赋给auto 变量,由变量来调用
auto f = [](int a){return a;};
f(123);
<4>.把表达式赋给函数指针,由指针调用
using func = int(*)(int);
func f = [](int a){return a;};
f(123);
三:using 和 typedef
using 可以代替 typedef
using 可以定义别名模板,typedef不可以
1.函数指针的定义区别
typedef int(*func_t)(int);
using func_t int(*)(int);
2.一个模板类因为有模板参数,所以该类不可能直接用typedef,typedef只能重定义一个明确的类
template<class T>
class Object
{};
-----------------------------
typedef Object newClass; //错误,因为模板能数的原因
template<class T> using NewT = struct Object<T>;//using 的用处比typedef强的地方在此
附:typedef处理模板类的传统方法
template<class T>
class Object
{
public:
typedef Object<T> NewT;
};
Object::NewT t;//定义变量
四:函数回调
函数回调主要关注以下几类
1.函数指针
void (*funcptr)(void) = &func;
funcptr();
2.类成员函数指针
struct A
{
void memfun(void)
{
}
};
void (A::*memfuncptr)(void) = &A::memfun;
A a;
(a.*memfuncptr)();
3.仿函数
struct foo
{
void operator()(void)
{
}
};
foo f;
f();
4.静态成员函数
(有点复杂)
五.std::function函数包装器的用法
除类成员函数外,可直接用静态函数,普通函数,仿函数进行赋值
切记:不能容纳类成员函数
1.绑定普通函数
std::function<void(void)> f = func;
2.绑定静态函数
std::function<void(void)> f = Foo::foo;
3.绑定仿函数
Foo b;//具有仿函数的对像
std::function<void(void)> f = b;//绑定仿函数,注意生命周期
4.绑定类成员函数,必须要用std::bind函数才行
切记:传的都是指针
std::function<void(void)> f = std::bind(&Foo::fun,&foo);
六.std::bind函数绑定器的用法
说明:std::bind实际上生成了一个仿函数,可绑定部分参数,也可以不绑定
1.我们可以将该仿函数直接赋值给std::function
std::function<void(void)> f = std::bind(&Foo::fun,&foo);
2.std::bind既然是生成一个仿函数,可以自身执行而不依赖std::function
std::bind(output)(1,2);//可以直接生成函数调用
std::bind(output,1,2)();//上下效果一样
2.std::bind可以绑定std::function能包装的各种函数类型
普通函数
静态函数
仿函数
类成员函数
std::function<void(void)> f = std::bind(&Foo::fun,&foo);
类成员(比较复杂,可以把一个类的成员对像绑定成一个返回该对像的引用的函数,然后可以为此引用赋值,以修改该引用)
std::function<int&(void)> f = std::bind(&Foo::i,&foo);
f() = 12;
3. 参数占位std::placeholders::_1-_8
意义:std::bind(函数,原函数参数1,原函数参数2,原函数参数3...)扩号中的参数出现的位置顺序和原函数一样的顺序
std::placeholders::_1-_8参数占位符主要是把生成的仿函数的位置对原函数的位置
如:
std::bind(output,std::placehoders::_1,std::placehoders::_2)(1,2);//即output(1,2);
std::bind(output,std::placehoders::_2,std::placehoders::_1)(1,2);//即output(2,1);
std::bind(output,5,std::placehoders::_2)(1,2); //即output(5,2);
4. 组合函数的用法,暂且不管,太复杂了
七 .线程std::thread
线程用法很简单
创建方法(线程一创建就开始运行,参数在构造函数时传)
1.用函数直接构造创建
std::thread t(func);
2.用std::function间接构造创建
std::function<void(void)> f(func);
std::thread t(f);
3.用std::move移动构拷贝构造或是赋值构造
std::thread t2 = std::move(t);
std::thread t2(std::move(t));
移动后的原来线程不能再调用join之类的函数,否则为崩
4.用类成员函数构造
TPrintf tb;
std::thread t3(&TPrintf::Printf, &tb);
疑问:对像的生命周期和线程生命周期不一样怎么办
5.线程其它用法和函数
.join() 同步阻塞等待
.detach() 分离线程不阻塞,分离后的生命周期目前测出和主线程周期一样
.get_id() 得到ID
std::thread::hardware_concurrency() 得到CPU个数
std::this_thread::sleep_for(std::chrono::seconds(3)); 线程睡眠
八.线程互斥
互斥分为可重入和非可重入,超时和非超时,C11提供4种互斥
为了与过去保持技术上的一致,只掌握非可重入的不带超时的std::mutex
1.std::mutex实际上是之前自己封装的互斥锁,现在C11已经直接提供加锁和解锁
.lock();//加锁
.unlock();//解锁
2.std::lock_guard自动加锁,传入互斥对像构造即可,离开作用域自动解锁
std::lock_guard(std::mutex& mutex);
九.条件变量
要求任何在等待该条件变量的线程必须先获取锁才能等待该条件
1.所有互斥量,自动析构锁
std::condition_variable_any std::lock_guard std::mutex/std::timed_mutex...
2.只对std::mutex有效,并且只能用std::unique_lock
std::condition_variable std::unique_lock std::mutex
用法:两者完全一样的用法,std::condition_variable的性能要高一点
两都都是在加锁成功后,等待一个条件变量,等待过程中释放锁,条件满足后重新加锁
.wait(std::mutex& mutex);
.wait(std::mutex& mutex,lambda);
.notyfy_once();
.notyfy_all();