-
整形提升的隐式转换
- 5种标准整型:signed char、short int、int、long int、long long int
- 等级
- 长度越大的整型等级越高:long long int 高于 int
- 长度相同时标准整型的等级 高于 扩展类型:long long int 高于 int64
- 转换规则
- 低级 转 高级
- 有符号 转 无符号
-
类模板与函数模板
- c++11前不支持函数模板的默认参数,只支持类模板的默认参数。c++11后就支持了
- 当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。
- 但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化。
-
字符串与数值类型的转换(头文件string)
- 转为字符串:to_string
- 转为数值:stoi、stof
- 如果字符串的前半部分字符是数值类型,后半部不是,那么前半部分会被转换为对应的数值返回
-
静态断言
- 在编译阶段进行检测,且不需要引入头文件
static_assert(sizeof(long) == 8, "错误, 不是64位平台...");
- 在编译阶段进行检测,且不需要引入头文件
-
noexcept
-
c++11以前,在函数后面跟throw(),不带参数表示不抛出异常
-
c++11中,使用noexcept或者noexcept(true)表示不抛出异常
#include <iostream> #include <string> using namespace std; struct MyException { MyException(string s) :msg(s) {} string msg; }; double divisionMethod(int a, int b) noexcept { if (b == 0) { throw MyException("division by zero!!!"); // throw 100; } return a / b; } int main() { try { double v = divisionMethod(100, 0); cout << "value: " << v << endl; } catch (int e) { cout << "catch except: " << e << endl; } catch (MyException e) { cout << "catch MyException: " << e.msg << endl; } return 0; }
-
-
auto的限制
- 不能推导函数参数类型
int func(auto a, auto b);//error
- 不能推导 非静态非常量 的 成员变量
class Test { auto v1 = 0; // error static auto v2 = 0; // error const auto v3 = 0; // error static const auto v4 = 10; // ok }
- 不能用来定义数组
auto t3[] = {1,2,3,4,5}; // error, auto无法定义数组
- 不能推导模板参数
- 不能推导函数参数类型
-
nullptr
- 在c中,NULL等价于0,这导致函数重载时,如果传入NULL,无法分辨是空指针还是0
- 因此,c++中使用nullptr来表示空指针,NULL和0都表示数值0
-
lambda表达式也是c++11引入的
-
constexpr
- 此前const有2重意思。区分 变量只读 与 常量:
对于const int& b = a1; a1变了,b也会随之变化,因此b是只读的,但不是常量
- 引入constexpr后:使用 const表示只读,使用 constexpr表示常量
- 用于修饰函数时
- 正常得到常量表达式函数
constexpr int func1() { constexpr int a = 100;//函数内部不能出现非常量表达式 return a;//返回值必须是常量 }
- 修饰模板函数
如果模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略
- 修饰构造函数:直接得到一个常量对象
函数体必须为空、初始化列表的方式为各个成员赋值
- 正常得到常量表达式函数
- 此前const有2重意思。区分 变量只读 与 常量:
-
继承构造函数
-
using A::A;
在子类中这样写上,就相当于写了所有父类的构造函数,不用繁琐的在子类中重新定义和基类一致的构造函数了 -
using A::fun;
在子类中这样写上,就可以调用被子类隐藏的父类同名函数#include <iostream> using namespace std; class A { private: int m; public: A(int m) {this->m = m;}; ~A(){}; void fun(int m) { cout<<"fun1"<<endl; } void fun(int m, int n) { cout<<"fun2"<<endl; } }; class B: public A { public: using A::A; using A::fun; void fun(int m) { cout<<"child"<<endl; } }; int main() { B b(3); b.fun(2);//调B的fun b.fun(2,2);//调A的fun2 return 0; }
-
-
可调用对象
-
- 普通函数
-
- 函数指针
int (*func)(int, double) = &print;
- 函数指针
-
- 匿名函数
[](int a, int b) -> int{ cout << a << " * " << b << " = " << a * b << endl; };
- 匿名函数
-
- 仿函数:重载了operator()的类对象
-
- 重载类型转换操作符,使其返回函数指针
- 首先定义一个函数指针
using func_ptrxx = void(*)(int, string);
- 然后重载类型转换操作符operator type()
operator func_ptrxx() { return print;//注意print函数必须是static的 }
-
- 是类成员函数指针 或 类成员指针
#include <iostream> using namespace std; using func_ptrxx = void(*)(int, string); struct Test { void yx(int a, string b) {cout<<"fun"<<endl;} int m; }; int main(void) { void (Test::*func_ptr)(int, string) = &Test::yx;//类成员函数指针 int Test::*obj_ptr = &Test::m;//类成员指针 return 0; }
-
-
可调用对象包装器:可以容纳除了类成员(函数)指针之外的所有可调用对象
#include <functional>
std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
// 绑定一个普通函数 function<int(int, int)> f1 = add; // 绑定以静态类成员函数 function<int(int, int)> f2 = T1::sub; // 绑定一个仿函数 T2 t; function<int(int, int)> f3 = t; //绑定lambda表达式 function<int(int, int)> f4 = [](int a, int b) -> int{ cout << a << " * " << b << " = " << a * b << endl; }; // 函数调用 f1(9, 3); f2(9, 3); f3(9, 3); f4(9, 3);
-
可调用对象包装器作为回调函数
#include <iostream> #include <functional> using namespace std; class A { public: //常量左值引用可以引用右值 // A(const function<void()> &f): callback(f) // { // } //右值引用可以引用右值,延长右值的声明周期 // A(function<void()> &&f): callback(f) // { // } //传入的右值被调用移动构造来创建f,而不是拷贝构造 A(function<void()> f): callback(f) { } void notify() { callback(); } private: function<void()> callback; }; int main(void) { A a([](){ cout<<"dd"<<endl; });//传入右值 a.notify(); return 0; }
-
绑定器bind
在传绑定的参数时,如果想传引用,必须使用ref包起来,如果想传const引用,使用cref
void f(int& n1, int& n2, const int& n3)
bind(f, n1, ref(n2), cref(n3));//ref传引用,cref传const引用
-
绑定非类成员函数
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
#include <iostream> #include <functional> using namespace std; void callFunc(int x, int y, const function<void(int, int)>& f) { f(x,y); } void output(int x, int y) {cout << x / y << endl;} int main(void) { // 使用绑定器绑定可调用对象和参数 auto f1 = bind(output, placeholders::_2, placeholders::_1); callFunc(2, 3, f1); return 0; }
-
绑定类成员函数
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
注意:对于非类成员函数,函数名就是函数的地址;
但是,对于类成员函数,它的函数名表示成员函数相对于这个类的地址。
function<void(int, int)> f1 = bind(&Test::output, &t, placeholders::_1, placeholders::_2);
-
-
改进了friend语法
- 声明一个类为另外一个类的友元时,不再需要使用class关键字,并且还可以使用类的别名
friend class Tom; // C++98 标准语法
friend Tom; // C++11 标准语法
- 可以把类模板T声明成友元
#include <iostream> using namespace std; template<class T> class B { public: friend T; private: int m=3; }; class A { public: void fun() {cout<<b.m<<endl;} private: B<A> b; }; int main() { A a; a.fun(); return 0; }
- 声明一个类为另外一个类的友元时,不再需要使用class关键字,并且还可以使用类的别名
-
强类型枚举:加关键字class或者struct
- 此前的枚举值会默认从0开始依次赋值,且枚举成员的名字全局可见,易冲突。
- c++11中提出强类型枚举,使得各枚举值有自己的作用域,且无法隐式类型转换为int
#include <iostream> using namespace std; enum class Color{red,black};/底层类型值默认为int enum class Name:char{nihao,hh}; int main() { //int res = Color::red;强类型枚举值不再能够作为整型使用了 cout<<sizeof(Color::red)<<endl;//4 cout<<sizeof(Name::nihao)<<endl;//1 return 0; }
- 对原来的枚举进行了扩展。使其也能指定底层类型。枚举成员的名字除了会自动输出到父作用域,也可以在枚举类型定义的作用域内有效。
#include <iostream> using namespace std; enum Color:char{red,black}; // enum Name:char{nh,black};//black重定义 int main() { cout<<sizeof(red)<<endl; cout<<sizeof(Color::red)<<endl; return 0; }
-
POD类型
- 分为2个类型:平凡的、标准布局的
- 平凡的类(结构体)需要满足
- 使用编译器生成的默认构造函数、析构函数、拷贝构造、移动构造、拷贝赋值、移动赋值
- 不含虚函数、不含虚基类
- 标准布局的类(结构体)需要满足
- 非静态成员的访问权限一样
- 派生类中有非静态成员,基类中包含静态成员(或基类没有变量)或者 基类有非静态成员,而派生类没有非静态成员。
- 子类第一个非静态成员 不要是 父类类型的对象
- 不含虚函数、不含虚基类
- 平凡的类(结构体)需要满足
- 判断是否为平凡类型:使用std::is_trivial的成员value来判断
cout << "int: " << is_trivial<int>::value << endl;
- 判断是否为标准布局类型:使用std::is_standard_layout的成员value来判断
cout << "int: " << is_standard_layout<int>::value << endl;
- 分为2个类型:平凡的、标准布局的
简记:POD类型被划分为2种,一种是平凡的,一种是标准布局的。如果一个类使用的构造析构移动拷贝赋值都是默认的,且没有虚函数,也没有虚基类,那么就是平凡的类型…
-
非受限联合体
- 之前的联合体不允许有静态成员、不允许有引用类型的成员、不允许有非POD类型的成员
- 现在,只要不是引用类型都可以作为联合体的数据成员
-
placement new的使用
ClassName* ptr = new (定位的内存地址)ClassName;
使用placement new,我们可以反复动态申请到同一块堆内存,这样可以避免内存的重复创建销毁,从而提高程序的执行效率
-
智能指针
weak_ptr与shared_ptr一起使用,unique_ptr不能生成weak_ptr
weak_ptr有失效检测
传递智能指针时都按值传递,不要按引用传递
头文件 #include <memory>
-
std::shared_ptr:共享的智能指针
-
共享智能指针的初始化方式
#include <iostream> #include <memory> using namespace std; int main() { int *a = new int; shared_ptr<int> ptr1(a);//通过构造函数初始化,后面参数还能再写一个删除器 cout<<ptr1.use_count()<<endl;//1 shared_ptr<int> ptr2(ptr1);//通过拷贝构造初始化 cout<<ptr1.use_count()<<endl;//2 cout<<ptr2.use_count()<<endl;//2 shared_ptr<int> ptr3(move(ptr1));//通过移动构造初始化 cout<<ptr1.use_count()<<endl;//0 cout<<ptr2.use_count()<<endl;//2 return 0; }
-
使用make_shared 完成内存对象的创建的同时,将其初始化给智能指针
make_shared<数据类型>(构造函数的参数列表)
#include <iostream> #include <memory> using namespace std; class A { public: A(int m):m(m) { cout<<m<<endl; }; private: int m; }; int main() { shared_ptr<int> ptr1 = make_shared<int>(10); shared_ptr<A> ptr2 = make_shared<A>(20);//20 return 0; }
-
reset的使用
重置ptr1,使其不指向任何对象:ptr1.reset()
重置ptr2,使其指向新的对象,并删除之前的对象:ptr2.reset(其它指针)
重置ptr3,使其指向新对象,并使用自定义的删除器来删除之前的智能指针:ptr3.reset(newPtr, CustomDeleter())
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> ptr1 = make_shared<int>(10); shared_ptr<int> ptr2 = ptr1; shared_ptr<int> ptr3 = ptr2; cout<<ptr1.use_count()<<endl;//3 cout<<ptr2.use_count()<<endl;//3 cout<<ptr3.use_count()<<endl;//3 //指向新的对象,引用计数减少了 int *b = new int; ptr3.reset(b); cout<<ptr1.use_count()<<endl;//2 cout<<ptr2.use_count()<<endl;//2 cout<<ptr3.use_count()<<endl;//1 //使用自定义的删除器 ptr2.reset(b, [](int * p){ delete p; cout<<"已释放"<<endl; }); return 0; }
-
get()获取原始指针
int *a = ptr1.get();
//实际使用时一般不用get,而是直接用ptr1->来操作原始对象 #include <iostream> #include <memory> using namespace std; class A { public: void fun(){cout<<m<<endl;} int m=5; }; int main() { shared_ptr<A> ptr1 = make_shared<A>(); ptr1->m = 3; ptr1->fun(); return 0; }
-
对于数组指针,需要指定删除器
使用shared_ptr管理动态数组时需要自定义删除器,因为它的默认删除器不支持数组对象
或者指定删除器为default_delete<int[]>() 即可删除数组
#include <iostream> #include <memory> using namespace std; class A { public: A(){cout<<"构造"<<endl;} ~A(){cout<<"析构"<<endl;} }; int main() { A* a = new A[5]; shared_ptr<A> ptr1(a, [](A* ptr){ delete[] ptr; }); //等价于 //shared_ptr<A> ptr1(a, default_delete<A[]>()); return 0; }
-
-
std::unique_ptr:独占的智能指针
- 和共享智能指针的使用差不多,只不过不允许复制(其删除了拷贝构造函数),而且指定删除器的时候需要给出删除器的类型。
#include <iostream> #include <memory> using namespace std; class A { public: A(){cout<<"构造"<<endl;} ~A(){cout<<"析构"<<endl;} }; int main() { A* a = new A[5]; //函数指针 返回类型(*指针变量名)(参数列表) using func_ptr = void(*)(A*); unique_ptr<A, func_ptr> ptr1(a, [](A* ptr){ delete[] ptr; }); //等价于 // unique_ptr<A, default_delete<A[]>> ptr1(a, default_delete<A[]>()); return 0; }
- 上述代码中使用lambda表达式写的删除器没有捕获外部变量,因此会转换成函数指针,但是改为捕获外部变量就会报错,需要使用可调用对象包装器。
A* a = new A[5]; //使用可调用对象包装器来处理即可 unique_ptr<A, function<void(A*)>> ptr1(a, [&](A* ptr){ delete[] ptr; });
- 和共享智能指针的使用差不多,只不过不允许复制(其删除了拷贝构造函数),而且指定删除器的时候需要给出删除器的类型。
-
std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的
-
初始化:通过共享指针、其它弱引用指针
#include <iostream> #include <functional> #include <memory> using namespace std; class A { public: A(){cout<<"构造"<<endl;} ~A(){cout<<"析构"<<endl;} }; int main() { shared_ptr<A> ptr1 = make_shared<A>(); weak_ptr<A> w_ptr1 = ptr1;//隐式类型转换 weak_ptr<A> w_ptr2(w_ptr1); cout<<"引用计数"<<w_ptr2.use_count()<<endl;//1 cout<<"w_ptr2所监测的共享指针管理的那个原始对象是否已释放"<<w_ptr2.expired()<<endl;//0 //通过弱引用指针来创建共享指针 shared_ptr<A> ptr2 = w_ptr2.lock(); cout<<"引用计数"<<w_ptr2.use_count()<<endl;//2 w_ptr2.reset();//不再监测 return 0; }
-
使用shared_from_this()返回共享智能指针
struct Test : public enable_shared_from_this<Test> { shared_ptr<Test> getSharedPtr() { return shared_from_this(); } };
-
-
使用场景
- 当需要在多个函数中共享同一个资源,可以使用共享指针来管理。在所有函数都使用完该资源后,共享指针引用计数变为0,就会自动释放所管理的资源。
- 如果需要自定义某指针释放时的一系列操作,那么就使用共享指针来管理,并指定删除器。
- 提前释放:直接令智能指针为nullptr,那么会自动释放,不会导致重复释放
-
-
chrono库
- 时间间隔duration:记录时间长度
头文件:<chrono>
#include <chrono> #include <iostream> using namespace std; int main() { //duration的使用 chrono::duration<int, ratio<6,2>> h(2);//3s为一个单位,h为2个单位//ratio默认为1 cout<<h.count()<<endl;//count()返回h表示几个单位,表示2个单位 //其它时间间隔 chrono::milliseconds h1 = h;//把h转为以ms为单位,即2*3000 cout<<h1.count()<<endl;//6000ms就是6000个单位 //计算 chrono::seconds h2{2};//2s 可以采用初始化列表的方式赋值 chrono::milliseconds res = h1-h2;//6000ms-2s cout<<res.count()<<endl;//4000 //不同ratio的duration之间的运算 chrono::duration<double, ratio<9, 7>> d1(3); chrono::duration<double, ratio<6, 5>> d2(1); chrono::duration<double, ratio<3, 35>> d3 = d1 - d2;//ratio<最大公约数,最小公倍数> }
- 时钟clocks (在头文件chrono的命名空间chrono里)
- 时间点time point
- 系统时钟 system_clock
当前系统时间点:system_clock::time_point today = system_clock::now()
time_point转time_t类型:time_t tm = system_clock::to_time_t(today);
time_t类型转time_point:system_clock::time_point ss = system_clock::from_time_t(tm);
- 固定时钟(用于记录程序耗时) steady_clock
steady_clock::time_point start = steady_clock::now();
注意,时间点差值结果是纳秒的duration:
auto dt = last - start;
cout << "总共耗时: " << dt.count() << "纳秒" << endl;
- 更高精度的high_resolution_clock,使用方法与steady_clock一样
- 转换函数
- duration_cast
nanoseconds res = steady_clock::now()-m_alivetime; //使用duration_cast将纳秒转换成毫秒 milliseconds millsec = duration_cast<milliseconds> (res);
- time_point_cast
- duration_cast
- 系统时钟 system_clock
- 时间间隔duration:记录时间长度
-
多线程
-
创建线程对象:
thread(任务函数名,任务函数的参数);//函数名就是函数地址
如果任务函数是类成员函数,那么第2个参数需要传 实例对象的地址,否则新线程无法影响到主线程的类实例。thread t(&A::fun2, &a, 函数参数);
对于非类成员函数,函数名就是函数地址,因此 thread t2(fun)即可
对于类成员函数,它的函数名表示成员函数相对于这个类的地址,因此函数名要加&- 如果任务函数的参数是引用类型,那么这边参数传过去时要用ref来进行引用传递
#include <iostream> #include <thread> void f2(int& m) { m++; std::cout << "ref:: m= " << m << std::endl;//11 } int main() { int m = 10; //std::thread m_T1(f2, m);//报错 std::thread m_T1(f2, std::ref(m)); m_T1.join(); std::cout << "m_T1:: m= " << m << std::endl;//11 }
- 如果任务函数的参数是引用类型,那么这边参数传过去时要用ref来进行引用传递
-
获取当前线程id:
this_thread::get_id()
-
线程休眠:
- 休眠一段时间:
this_thread::sleep_for(chrono::seconds(1));
- 休眠至某时间点:
this_thread::sleep_until(now + sec);
- 休眠一段时间:
-
让出cpu变为就绪态:
this_thread::yield();
-
等待线程t结束才继续执行:
t.join();
-
线程分离:线程执行完会自己释放资源,不再由thread对象管理:
t.detach();
-
不能复制线程资源,只能移动:
thread t2 = move(t1);//t1此后没用了
#include <iostream> #include <thread> using namespace std; void thread_fun1(int m) { cout<<"xx:"<<m<<endl; int i=5; while (i--) { cout<<i<<endl; this_thread::sleep_for(chrono::seconds(1));//休眠1s } } int main() { thread t1(thread_fun1, 5); thread t2 = move(t1);//t1没用了 t2.join(); cout<<"结束"<<endl; return 0; }
-
-
call_once的使用
头文件 <mutex>
//要保证该对象可以被多个线程同时访问到 once_flag g_flag; //把这个对象传入。后面是 执行一次的函数与其函数参数 call_once(g_flag, do_once, 19, "luffy");
-
互斥锁mutex
-
创建锁:mutex g_num_mutex;
-
加锁:g_num_mutex.lock();
- 如果无法保证多个锁的上锁顺序,可以使用std::lock(mutex1, mutex2,…)来同时上锁多个,这样不会因为线程调用顺序导致死锁。
- std::lock有个对应的 会自动调用解锁的版本:std::lock_guard
-
尝试加锁(不阻塞):g_num_mutex.try_lock();
-
解锁:g_num_mutex.unlock();
-
c++11新增了哨兵锁,析构时解锁:lock_guard lock(g_num_mutex);
-
unique_lock析构时也自动解锁,同时还能手动释放锁
lock_guard unique_lock 构造函数中获取锁,析构函数中释放锁 构造函数中获取锁,析构函数中释放锁 将拷贝构造函数和赋值运算符重载函数delete掉了,也就是说lock_guard将 不能作为函数参数传递或者函数的返回值; 将拷贝构造函数和赋值运算符重载函数delete掉了,但新增了带右值引用的拷贝和赋值,可以在函数传参或者参数返回中使用该锁 使用了智能指针的思想,出作用域自动释放锁。 提供了lock()和unlock()方法使得能够与条件变量搭配使用 -
递归互斥锁允许一个线程多次获取同一个互斥锁:recursive_mutex
-
超时锁:timed_mutex g_mutex;
chrono::seconds timeout(1); g_mutex.try_lock_for(timeout)//返回true表示抢到锁了
-
读写锁
c++14引入了限时读写锁shared_timed_mutex,该锁有普通的排他性锁定lock(),也有共享锁定lock_shared()//创建限时读写锁 std::shared_timed_mutex stmtx; //读 void ReadThread() { stmtx.lock_shared();//共享锁定 //读取数据... stmtx.unlock_shared(); } //写 void WriteThread() { stmtx.lock();//排他锁定 //写数据... stmtx.unlock(); }
同时,c++14还引入了shared_lock包装器用于包装带lock_shared的锁,使得不用手动释放读写锁。shared_lock的lock是共享锁定的,unique_lock的lock是排他锁定的,因此,一般像下面这样使用
//创建限时读写锁 std::shared_timed_mutex stmtx; //读 void ReadThread() { std::shared_lock<std::shared_timed_mutex> lock(stmtx); //读取数据... } //写 void WriteThread() { std::unique_lock<std::shared_timed_mutex> lock(stmtx); //写数据... }
c++17引入了读写锁shared_mutex,与shared_timed_mutex类似,只不过少了几个限时的函数。
-
-
c++11提供了2种条件变量
- condition_variable
头文件<condition_variable>
- 需要使用unique_lock锁,而不能用lock_guard锁,因为该锁只会在析构时释放
- 条件变量:
condition_variable m_notFull;
- 阻塞
- 无条件阻塞:
m_notEmpty.wait(locker);
- 带条件阻塞:等价于while(m_queue.size() ==m_maxSize){m_notFull.wait(locker);}
m_notFull.wait(locker, [this]() {return m_queue.size() !=m_maxSize;});
- 无条件阻塞:
- 唤醒:
m_notEmpty.notify_one();
- condition_variable_any
-
传入wait的不是unique_lock锁,而是普通的互斥锁mutex m_mutex;
#include <iostream> #include <thread> #include <mutex> #include <functional> #include <condition_variable> using namespace std; class A { public: void xiaofei() { while (1) { //使用条件变量,必须用这个锁 unique_lock<mutex> locker(m_mutex); //相当于while(!后面的条件){wait} not_empty.wait(locker, [this](){ return m > 0;//这里是不阻塞的条件 }); m--;//消费 not_full.notify_all();//唤醒生产者 cout<<"消费,当前m="<<m<<endl; this_thread::sleep_for(chrono::seconds(1));//线程休眠 } } void shengchan() { while (1) { unique_lock<mutex> locker(m_mutex); not_full.wait(locker, [this](){ return m < maxnumber; }); m++;//生产 not_empty.notify_all();//唤醒消费者 cout<<"生产,当前m="<<m<<endl; this_thread::sleep_for(chrono::seconds(1)); } } private: int m=3;//资源 int maxnumber=5;//资源上限 mutex m_mutex;//锁 condition_variable not_empty;//条件变量 condition_variable not_full; }; int main() { A a; thread t1(&A::xiaofei, &a); // thread t1(std::bind(&A::xiaofei, &a)); thread t2(&A::shengchan, &a); t1.join(); t2.join(); return 0; }
-
- condition_variable
-
多线程异步操作
头文件#include <future>
- promise类
- 创建promise
promise<int> pr;
- 将其作为函数参数传给线程的函数
thread t1([](promise<int> &p) { //任务函数经过一系列操作,得到结果100,将结果存入promise对象 p.set_value(100);//这里修改了promise存储的value值,因此需要传引用 }, ref(pr));//当任务函数参数是引用类型时,这里需要使用ref包起来
- 从promise对象中取出future对象
future<int> f = pr.get_future();
- 从future对象中取出子线程任务函数中返回的值
int value = f.get();//阻塞,直到取得数据
- 区分set_value 和 set_value_at_thread_exit
- set_value :在子线程执行期间就把值返回,因此f.get会提前取得数据解除阻塞
- set_value_at_thread_exit:子线程执行结束后才返回,则主线程是在子线程结束后才能解除f.get阻塞
- 创建promise
- packaged_task
- 创建packaged_task对象:创建方法类似可调用对象包装器
packaged_task<int(int)> task([](int x) { return x += 100; });
- 把packaged_task对象的引用作为任务函数传入线程
thread t1(ref(task), 100);
- 从packaged_task对象中取出future对象
future<int> f = task.get_future();
- 创建packaged_task对象:创建方法类似可调用对象包装器
- async
#include <iostream> #include <thread> #include <future> using namespace std; int main() { // 1.调用函数直接创建线程执行任务(即使用默认策略launch::async) future<int> f = async([](int x) { //... return x += 100; }, 100); cout << "子线程返回值: " << f.get() << endl; //2.使用策略launch::deferred,不创建新线程 future<int> f2 = async(launch::deferred, [](int x){ //... return x+= 100; }, 200); cout << "子线程返回值: " << f2.get() << endl; return 0; }
- promise类