[学习][记录] c++语言:从放弃到入门 <一> c++11新关键字以及引入的新特性
参考 c++11新特性,所有知识点都在这了!
文章目录
- 一、nullptr
- 1.1 入参
- 1.1.1 解释
- 1.1.2 办法
- 1.2 返值
- 二、override
- 2.1 含义
- 2.2 目的
- 2.3 问题?
- 三、final
- 3.1 含义
- 3.2 意义
- 四、 =default =delete
- 4.1 default
- 4.2 delete
- 4.3 Raw String Literals
- 4.3.1 缘由
- 4.3.2 使用
- 五、Range-Based for 循环Foreach
- 5.1 语法形式:
- 5.2 解释:
- 5.3 关于vector
- 六、std::for_each()
- 七、STL容器的使用
- 7.1 normal init 常规初始化
- 7.1.1 vector
- 7.1.2 list
- 7.1.3 map
- 7.1.3.1 查找
- 7.1.3.2 插入
- 7.1.3.3 删除
- 7.2 initialization List {} 列表方式初始化
- 7.3 容器的使用
- 7.3.1 std::map
- 插入
- 查找
- 删除
- 7.3.2 std::vector
- 插入
- 查找
- 删除
- 7.3.3 std::list
- 插入
- 查找
- 删除
- 八、initializer_list 类
- 8.1 原理
- 8.2 常见用法:
- 8.2.1 普通函数参数
- 8.2.2 构造器参数
- 8.3 格式
- 九、auto 自动类型推导
- 9.1 注意
- 9.2 应用
- 十、declrtype
- 10.1 推导规则
- 10.2 应用
- 十一、仿函数
- 11.1 operator()
- 11.2 带状态的 operator()
- 十二、Lambda
- 12.1 匿名函数
- 12.2 Gammar 格式
- 12.2.1 格式
- 12.2.2 解释
- 十三、 enum和enum class(struct)
- 13.1 C语言与c++:
- 13.2 C++->C++11:
- 13.3 问题描述
- 13.4 作用域
- 13.4.1 类内的枚举
- 13.4.2 带作用域的枚举
- 13.5 指定类型
- 十四、 assert/static_assert
- 14.1 assert 运行期断言,它用来发现运行期间的错误
- 14.2 static_assert(提前判误)
- 十五、引用
- 15.1 右值引用定义 (r-value ref) &&
- 15.1.1 传引用和传指针,传右值引用
- 15.1.1.1 传引用
- 15.1.1.2 传指针
- 15.1.1.3 传右值引用
- 15.2 const T & 万能常引用
- 15.2.1 const 一些常见用法解释
- std::ref
- 十六、 深赋值与浅赋值
- 十七、 深拷贝与浅拷贝
- 十八、 移动构造
- 18.1 慎用移动
- 十九、 std::move
- 19.1 实质
- 19.2 应用
- 19.3 移动构造在STL中的应用
- 二十、 std::function
- 20.1 含义
- 20.2 应用
- 20.2.1 function 作参数类型实现回调
- 20.2.1.2 function 作类成员实现回调
- 二十一、 std::bind
- 21.1 含义
- 21.2 作用
- 21.2.1 绑定普通函数与参数及占位
- 21.2.2 绑定对象与成员及占位
- 21.2.3 函数重载情形
- 21.3 多态之 bind +fucntion
- 21.3.1.可实现多态使用
- 21.3.2.function 本是不可以包装类成员函数,bind实现类成员函数的绑定, 然后赋给 fucntion 对象,亦即实现了间接性的包装
- 二十二、 Unordered Contrainer 无序容器
- 22.1 unordered_map
- 二十三、 Auto Memeory Manage 自动内存管理
- 23.1 RAII(Resource Acquisition Is Initialization)
- 23.2 auto_ptr(deprecated)
- 23.3 unique_ptr
- 23.3.1 常用函数
- 23.4 shared_ptr
- 23.5 用法
- 23.5.1 常用函数
- 23.6 weak_ptr
- 二十四、 Thread框架
- 24.1 join 与 detach
- 24.2 传参方式
- 24.3 常用函数
- 24.4 同步之mutex
- 24.5 volatile
- 24.6 lock(),unlock()
- 24.7 try_lock(),unlock()
- 24.8 std::lock_guard()
- 24.9 死锁
- 24.10 std::recursive_mutex()
- 24.11 同步之std::condition_variable
- 24.11.1 成员函数
- 24.12 std::unique_lock()
- 24.13 std::atomic
- 24.14 std::call_once
- 24.15 volatile相关
- 24.16 异步相关
- std::future
- std::promise
- std::packaged_task
- std::promise与std::future配合使用
- std::packaged_task与std::future配合使用
- 24.17 std::async
- 语法
一、nullptr
nullptr 是用于解决 NULL 和 0 的有疑义关系的。NULL 通常被义为(void*)0。在 如下应用中会引发歧义。
1.1 入参
#include <iostream>
using namespace std; void f(int){}
void f(bool){}
void f(void*){}
int main() {f(0); // 调用f(int)f(NULL); // 可能无法编译通过,但一般会调用f(int)f(nullptr); // 调用f(void*)
}
1.1.1 解释
- C++ 视 0 首先为 int 型,因此,调用 f(0) 即调用 f(int)
- NULL 的情况复杂些,C++ 首先视其为广义整型。假如 NULL 被定义为普通 的 0,则调用 f(int); 如果 NULL 被定义成 0L,则 long -> int, long -> bool, 0L -> void*, 这三 种情况都是合法的,此时,编译器会报错
- 使用 nullptr,则不会有重载函数调用模糊的问题 - nullptr 不属于广义整型,也不是普通意义上的指针。 - nullptr 的实际类型是 std::nullptr_t,它能够隐式的转换成所有的原始指针 类型,故可将其视为一个可指向所有类型的指针。
1.1.2 办法
避免NULL重载编译报错:
f((int)NULL) 通过强转的办法解决
1.2 返值
使用 0 与 result 作比较,则一时不能确定 findRecord 的返回值类型 (可能是 广义整型,也可能是指针类型); 使用 nullptr,可以清楚地知道 findRecord 的返回值, 必定是一个指针类型。
auto result = findRecord( /* arguments */ );if (result == 0) { ... }auto result = findRecord( /* arguments */ );
if (result == nullptr) { ... }
二、override
2.1 含义
覆盖重新写从父类继承过来的函数
覆写父类的虚函数时候,好的 IDE 一定会给出斜体等的提示,表示此函数覆写自 父类。
2.2 目的
避免发生,编译通过,但是逻辑错误的情况 例如函数名写错了 但是编译通过,却不是从父类继承过来的函数。
利用关键字 override 则指明,此种覆写关系,若此关系不成立,则以报错的形式提示 给用户。
2.3 问题?
class G{public:virtual void func(int) {printf("G::func(int) \n");};};class H:G{public://virtual void func(int) override{// printf("H::func(int) \n");//}virtual void func(double){printf("H::func(double) \n");}
};void main() {H *p = new H; p->func(5); p->func(5.0); system("pause");
}
vs2015
输出
H::func(double)
H::func(double)
请按任意键继续. . .
疑问???
没调用到G类的func(int)
三、final
3.1 含义
关键字 final 有两个用途:
第一,它阻止了子类继承;
class A final
{public: virtual void func() const;
};class B: A //错误 编译报错 A is final
{public: void func() const override final;// 错误 编译报错 A is final
{};
第二,阻止一个虚函数的覆 写。
class A
{public: virtual void func() const;
};class B
{public: void func() const override final;//OK
};class C: B {public:void func() const; //error, B::func is final 编译报错
};
3.2 意义
阻止类的无限扩展。
四、 =default =delete
4.1 default
C++ 的类有四类特殊成员函数,它们分别是:
- 默认构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值运算符。
class A {public:A();//构造函数 创建类的实例时 调用~A();//析构函数 销毁类的实例时 调用A(const A &) =;//拷贝构造函数 类实例之间的拷贝 例如A a;A b = a;//此时调用拷贝构造函数A operator=(const A &);//拷贝赋值运算符 本质重载运算符=进行初始化
}
关键字default
如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员 函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。
class A {public: A() = default;
A(int x ):_x(x) {}
private: int _x;
};
4.2 delete
为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性: "=delete"函数。程序员只需在函数声明后上“=delete;”,就可将该函数禁用。
class Singleton {public:static Singleton* getInstance() {if(_ins == nullptr) _ins = new Singleton; return _ins; }Singleton(Singleton &) = delete; Singleton& operator=(Singleton &) = delete;
private: Singleton(){} static Singleton* _ins;
};Singleton* Singleton::_ins = nullptr; //类的静态变量在类外初始化int main() { Singleton *ps = Singleton::getInstance(); Singleton ps(*ps); *ps = Singleton::getInstance(); return 0;
}
4.3 Raw String Literals
4.3.1 缘由
C/C++中提供了字符串,字符串的转义序列,给输出带来了很多不变,如果需要 原生义的时候,需要反转义,比较麻烦。
4.3.2 使用
C++提供了 R"()",原生字符串,即字符串中无转义,亦无需再反义。但是注意() 中的)"会导至提前结束。
#include <iostream> using namespace std;string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754"; // 错误,\没转义
string path2= "C:\\Program Files (x86)\\alipay\\aliedit\\5.1.0.3754"; //正确,\转义了 用\\或者/,但麻烦
string path3= R"(C:\Program Files (x86)\alipay\aliedit\5.1.0.3754)"; //正确,使用了R()
string path4= R"(C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754)";//正确,使用了R()//string path5= R"(C:\Program "Files" (x86)\\alipay\aliedit\)"5.1.0.3754)";//错误,R()的缺陷int main(int argc, char *argv[]) {cout << path.c_str() << endl;cout << path2.c_str() << endl;cout << path3.c_str() << endl;cout << path4.c_str() << endl;system("pause");return 0;
}输出结果:
C:Program Files (x86)lipayliedit.1.0.3754
C:\Program Files (x86)\alipay\aliedit\5.1.0.3754
C:\Program Files (x86)\alipay\aliedit\5.1.0.3754
C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754
五、Range-Based for 循环Foreach
一种基于STL容器遍历的一种for循环形式 类似Java的 foreach
5.1 语法形式:
for (declaration : expression) statement例如
vector<int> vecInt = {0,1,2,3,4,5}; //expression 列表
for(auto &i:vecInt){//auto &i is declaration 申明cout << i << endl;
}等价写法1
vector<int> vecInt = { 1,2,3,4,5 };
for (int i = 0; i<vecInt.size(); i++) {int tmp = vecInt.at(i);cout << tmp << endl;
}等价写法2
vector<int> vecInt = { 1,2,3,4,5 };
vector<int>::iterator itr = vecInt.begin();
for (; itr != vecInt.end(); itr++) {cout << *itr << endl;
}
5.2 解释:
expression 部分是一个对象,必须是一个序列,比方说
用花括号括起来的初始值
1.列表
2.数组
int[] a={1,2,3,4,5}; char ch[] = “123”;string str = “china”;
3.vector 或 string 等类型的对象。
vector vet = {1,2,3};list list= {1,2,3} map<int,string>map = {1,“123”};
这些类型的共同特点是拥有能返回迭 代器的 begin 和 end 成员。
declaration 部分负责定义一个变量,该变量将被用于访问序列中的基础元素。
每 次迭代,declaration 部分的变量会被初始化为 expression 部分的下一个元素值。
确 保类型相容最简单的办法是使用 auto 类型说明符。
5.3 关于vector
1.vector::size 会遍历vector 效率会低
六、std::for_each()
#include "stdafx.h"
#include<algorithm>
#include<iostream>
#include<vector>void func(int n)
{std::cout << n << std::endl;
}int main()
{std::vector<int> arr;arr.push_back(1);arr.push_back(2);std::for_each(arr.begin(), arr.end(), func);return 0;
}
七、STL容器的使用
7.1 normal init 常规初始化
所谓的常规初始化,即调用类的构 造器。
7.1.1 vector
vector<int> vi;
vector<int> vi(10,10);//size 10,each value 10
vector<int> vi(arr,arr+10);//begin,end
7.1.2 list
list<int> li(10);
list<int> li2(10);list<int> li3(10,1);//size 10,each value 1int arr2[10] = {};
list<int> li4(arr2,arr2+10);//begin,end
7.1.3 map
7.1.3.1 查找
7.1.3.2 插入
//map[key] = value
map<int,string> mis;
mis[0] = "first";
mis[1] = "second";
mis[2] = "third"; //make_pair
map<int,string> mis2(mis.begin(),mis.end());
mis.insert(std::make_pair(3, "fourth"));
mis.insert(pair<int, string>(3, "fourth"));//value_type
mis.insert(map<int, string>::value_type(3, "fourth"));
for (auto& pair : mis) cout << pair.first << ":" << pair.second.c_str() << endl;
7.1.3.3 删除
7.2 initialization List {} 列表方式初始化
以初始化列表的方式来进行实始化,打破了,原有的初始化格局,令实始化更直观 化,人性化。
vector<int> vi = {1,2,3,4,5};
list<int> li = {1,2,3,4,5};
map<int,string> mis = { {1,"c"}, {2,"c++"}, {3,"java"},{4,"scala"},{5,"python"}};mis.insert({6,"ruby"}); for(auto &is: mis) {cout<<is.first<<is.second<<endl;
}
7.3 容器的使用
7.3.1 std::map
插入
查找
删除
7.3.2 std::vector
插入
查找
删除
7.3.3 std::list
插入
查找
删除
八、initializer_list 类
8.1 原理
是使用 {} 而不是 () 调用构造函数,
template< class T > class initializer_list; C++11 中提供了新的模板类型 initializer_list。 initializer_list 对象只能用大括号{}初始化,其内有元素都是 const 的。常用于构造 器和普通函数参数。
8.2 常见用法:
8.2.1 普通函数参数
double sum(const initializer_list<double> &il)
{double s = 0; for(auto d:il)s += d; return s;
}double s = sum({1,2,3,4,5});
8.2.2 构造器参数
template <typename T>
class myarr {private: vector<T> _arr;
public: myarr() {cout<<"called myarr()"<<endl;}myarr(const initializer_list<T>& il) {cout<<"called myarr(const initializer_list<T>& il)" << endl; for (auto x : il)_arr.push_back(x); }
};int main() {myarr<int> ma;myarr<int> ma2 = {1,2,3,4,5}; return 0;
}
8.3 格式
={},({}),{};
会调用initilizer_list构造
int arr[] = {1,2,3};
int arr2[] {1,2,3};
vector<int> vi = {1,2,3};
vector<int> vi2{1,2,3};
九、auto 自动类型推导
9.1 注意
1.auto 能够实现类型的自我推导,并不代表一个实际的类型声明。auto 只是一个 类型声明的占位符
2. auto 声明的变量,必须马上初始化,以让编译器推断出它的实际类型,并在编译时将 auto 占位符替换为真正的类型。
3. c++11 auto 不能用于函数参数 c++14可以
9.2 应用
十、declrtype
auto 类型,作为占位符的存在来修饰变量,必须初始化,编译器通过初始化来确 定 auto 所代表的类型,即必须定义变量。 如果,我仅希望得到类型,而不是具体的变量产生关系,该如何作到呢?
10.1 推导规则
decltype(expr);
所推导出来的类型,完全与 expr 类型一致。
同 auto 一样,在编译期间完成,并 不会真正计算表达式的值,即上面的推导数并不会导致函数打印的。
expr 不可对类型 推导。
10.2 应用
十一、仿函数
重载了 operator()的类的对象,在使用中,语法类型于函数。故称其为仿函数。
11.1 operator()
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Compare {public: int operator()(int x, int y) {return x>y;}
};int main() {vector<int> vi = {1,3,5,7,9};sort(vi.begin(),vi.end(),Compare()); for(auto &i:vi) cout<<i<<endl;
}
11.2 带状态的 operator()
相对于函数,仿函数,可以拥用初始状态,一般通过 class 定义私有成员,并在声 明对象的时候,进行初始化。
#include <iostream>
#include <vector>
#include <algorithm> using namespace std;class Compare {public: Compare(bool f=true):flag(f){} int operator()(int x, int y) {if(flag) return x > y; else return x < y; }
protected: bool flag;
};int main() {vector<int> vi = { 1, 3, 5, 7, 9}; // sort(vi.begin(),vi.end(),Compare(false)); sort(vi.back(),vi.end(), Compare(true)); for(auto &i:vi) cout<<i<<endl;
}
十二、Lambda
12.1 匿名函数
简短函数,就地书写,调用,即 Lambda 存在的意义,常用于取代作回调用的简 短函数指针与仿函数。 就地书写,因只有函数体,即无函数名,也称匿名函数。
12.2 Gammar 格式
12.2.1 格式
[capturelist] (parameterlist) mutable ->return type { function body }
翻译一下
12.2.2 解释
1.capturelist 捕获列表。
捕获列表,总是出现在 lambda 函数的开始处。事实上[]是 lambda 的 引用符。换句话说,编译器根据引出符判断接下来的代码是否是 lamba 函数。
2.parameterlist 参数列表。
与普通函数的参数列表一致。如果不需要传递参数,可以连同()一起省略。
3.mutable 默认情况下,lambda 函数总是一个 const 函数, mutable 可以取消其常量性, 所谓的常量性是指不可修改 capturelist 中的东西。在使用该修饰符时,参数列表不可以 省略(即使参数为空)。
4.->return type 返回类型。 用于追踪返回类型形式声明函数的返回类型。出于方便,不需要返回值 的时候可以连同->一起省略。此外返回类型明确的情况下,也可以省略该部分。编译 器可以自行推导。
5.{ function body } 函数体。 内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获 的变量。
十三、 enum和enum class(struct)
13.1 C语言与c++:
enum 对象,是可以被其它非枚举值,赋值,而 C++中的枚举变量, 则只能用枚举值来赋值。
13.2 C+±>C++11:
1.枚举体的声明和定义使用 enum class 或是 enum struct, 二者是等价的。使用 enum class\enum struct 不会与现存的 enum 关键词冲突。而且 enum class\enum struct 具有更好的类型安全和类似封装的特性(scoped nature)。
13.3 问题描述
1 向整形的隐式转换(Implicit conversion to an integer)
2 无法指定底层所使用的数据类型(Inability to specify underlying type)
3 enum 的作用域(Scope)
4 不同编译器解决该问题的方法不统一
13.4 作用域
13.4.1 类内的枚举
class Painter {public: enum Color { red, green, yellow};public:Painter() {}void dis(Color c) {cout<<c<<endl; }
};
13.4.2 带作用域的枚举
enum class Color { red, green, yellow};
13.5 指定类型
默认的底层数据类型是 int,
用户可以通过:type(冒号+类型)例如 class colorX:char
来指定任何整形 (除了 wchar_t)作为底层数据类型。
#include <iostream>
using namespace std;#include <iostream>
enum class color:int { red, green, yellow};
enum class colorX:char { red, green, yellow };
int main() {//使用域运算符访问枚举体成员,强转后打印 std::cout << static_cast<int>(color::red) << std::endl; std::cout << static_cast<int>(colorX::red) << std::endl; return 0;
}
十四、 assert/static_assert
14.1 assert 运行期断言,它用来发现运行期间的错误
不能提前到编译期发现错误, 也不具有强制性,也谈不上改善编译信息的可读性,既然是运行期检查,对性能当然是 有影响的,所以经常在发行版本中,assert 都会被关掉。
#include <iostream>
#include <assert.h>
using namespace std;
char * myStrcpy(char *dest, const char *src) {assert(dest); assert(src); while(*dest++ = *src++); }int main(int argc, char *argv[]) {// char buf[1024]; char * p = NULL; // myStrcpy(buf,p); // cout<<buf<<endl; FILE *fp = fopen("aa.c","w"); assert(fp); return 0; }
14.2 static_assert(提前判误)
static_assert 这个关键字,用来做编译期间的断言,因此叫做静态断言。其语法 很简单:static_assert(常量表达式,提示字符串)。
如果第一个参数常量表达式的值为真(true 或者非零值),那么 static_assert 不做 任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该 static_assert 语句所在行,错误提示就是第二个参数提示字符串。
使用 static_assert,我们可以在编译期间发现更多的错误,用编译器来强制保证 一些契约,并帮助我们改善编译信息的可读性,尤其是用于模板的时候。 static_assert 可以用在全局作用域中,命名空间中,类作用域中,函数作用域中, 几乎可以不受限制的使用。 编译器在遇到一个 static_assert 语句时,通常立刻将其第一个参数作为常量表达 式进行演算,但如果该常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行 演算,这就让检查模板参数成为了可能。
十五、引用
15.1 右值引用定义 (r-value ref) &&
实质: 延长拷贝时产生的临时对象的生命周期
左值 指拥有稳定内存空间的内存区域 且拥有变量名
右值 指可能通过函数返回的临时变量 没有变量名 没有稳定内存空间 随时可能销毁的内存区域
左值引用 与 右值引用对比:
- 都属于引用类型。
- 都必须初始化。左值引用是具名变量值的别名,右值引用是匿名变量的别名。
- 左值引用,用于传参,好处在于,扩展对象的作用域。则右值引用的作用就在于 延长了临时对象的生命周期。
- 避免"先拷贝再废弃"带来的性能浪费。
- 对将亡值进行引用的类型;它存在的目的就是为了实现移动语义。
15.1.1 传引用和传指针,传右值引用
注意:1.栈对象的引用不可返回
栈对象返回,如果在没有优化的情况下-fno-elide-constructors,会产生临时对 象
在没有优化的情况下,要经历两次复制
2.拷贝不可避免
15.1.1.1 传引用
15.1.1.2 传指针
15.1.1.3 传右值引用
将函数返回的临时变量 延长 通过引用的方式 使用这个临时变量,减少拷贝次数
例如:
void func(string && str);string getStr(){string strTmp;return strTmp; //通过深拷贝strTmp的方式 返回一个tmp临时变量
}void main(){func(getStr());//tmp传入func 注意tmp 不是strTmp,只是一个拷贝的临时变量
}
如上 getStr()产生一个strTmp拷贝出来的临时变量tmp传入到func,func右值引用方式 延长tmp生命周期 使用tmp进行操作
15.2 const T & 万能常引用
func(const A &a)
特点:
const 可以实现常量引用,不同类型之引用(非基本数据类型也适用,但需要转化 构造函数)。 其本质也是产生了临时对象,并且该临时对象是 const 的。
缺陷:
可以在返回中避免临时对象,再次拷贝和销毁。但时临时对象的性质 是 const 的,也会给后续的使用带来不便。const 函数重载也是在这种反值 const 类型 的情型下诞生的。
func(A && a)
特点:减少拷贝次数,延长临时变量生命周期
缺陷: 对资源理解要深刻,慎用。
15.2.1 const 一些常见用法解释
std::ref
std::ref只是尝试模拟引用传递,并不能真正变成引用,在非模板情况下,std::ref根本没法实现引用传递,只有模板自动推导类型时,ref能用包装类型reference_wrapper来代替原本会被识别的值类型,而reference_wrapper能隐式转换为被引用的值的引用类型。
std::ref主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用
其中代表的例子是thread
比如thread的方法传递引用的时候,必须外层用ref来进行引用传递,否则就是浅拷贝。
C++11 std::ref和引用的区别
十六、 深赋值与浅赋值
【c++】深赋值与浅赋值
C++中的默认函数 深拷贝与浅拷贝 深赋值与浅赋值
十七、 深拷贝与浅拷贝
十八、 移动构造
对于类中,含有指针的情况,即含有堆内存资源的情况,要自实现其拷贝构造和拷 贝赋值。也就是所谓的深拷贝和深赋值。我想这己经成为一种共识了
实质:
将待返回对象的内容"偷"了过来。移 动构造的本质,也是一种浅拷贝,但此时的浅拷贝己经限定了其使用的场景,故而是安 全的。
18.1 慎用移动
拷贝,无疑是安全的,而移动,无疑是高效的。但是这种高效前提你对资源的深刻 的理解,否则可能带来极大的安全隐患的,所以要慎用。
移动,解决了,第一次拷贝的效率问题,
引用,解决了,第二次拷贝的效率 问题。
十九、 std::move
19.1 实质
move的名字本身比较迷惑,其本身并没有任何"移动",
它唯一的功能,就是将左 值转化为右值,完成移动语义,继而我们可以使用右值。
**左值:**具有稳定存储空间,有变量名,作用域中有效 例如 int a;
**右值:**具有不稳定存储空间,且无变量名,作为函数返回值的临时变量,用作拷贝到右值的中转存储,中转后即会销毁。
例如
int func(){int b = 0;return b;}void main(){int a = func();//func b拷贝一份到临时变量 tmp 再拷贝给a }
如果说,产生临时对象,隐式的调用了移动构造,
std::move是一种显示调用移动 构造的方法。即刻意营造一种可以使用移动的情景。
19.2 应用
可以 move 的类中,其父类成员,和成员对象的也要支持 move,就要借助 std::move。
#include <iostream>
using namespace std;
class Complex {public:Complex(int f = 0) :_f(new float(f)) {cout<<"Complex()"<<endl;}Complex(const Complex & another) :_f(new float(*another._f)) {cout<<"Complex(const Complex & another)"<<endl;}Complex(Complex && another) :_f(another._f) {another._f = nullptr;cout<<" Complex(Complex && another)"<<endl;}Complex& operator = (const Complex & another) {cout<<"Complex& operator = (const Complex & another)"<<endl;if(this != &another) {delete _f; _f = new float; *_f = *another._f;}return *this;}Complex& operator = ( Complex && another) {cout<<" Complex& operator = (const Complex && another)"<<endl;if(this != &another) {delete _f; _f = another._f;}return *this;}~Complex() {if(_f != nullptr)delete _f;}float *_f;
};
class Moveable {public:Moveable(int i) :_i(new int(100)),_c(2.1) //先父类,再类对象,再本类{cout<<" Moveable(int i)"<<endl;}Moveable(const Moveable &another ) :_i(new int(*another._i)) ,_c(another._c) {cout<<" Moveable(const Moveable &another )"<<endl;}Moveable(Moveable &&another) {cout<<" Moveable(Moveable &&another)"<<endl;_i = another._i;another._i = nullptr;_c = std::move(another._c); //走 Complex 移动赋值}~Moveable(){if(_i != nullptr) delete _i;}int *_i;Complex _c; //类对象成员
};
int main(int argc, char *argv[]) {Moveable m(100);Moveable n(std::move(m)); //走移动构造return 0;
}
19.3 移动构造在STL中的应用
二十、 std::function
include <functional>
20.1 含义
回调函数(包括,普通函数,函数指针, lambda,仿函数)的统一包装,相当于函数的抽象类或者接口,可以用统一的方式保存。
用法:
std:function<retType(argType,argType,…)>
retType 函数返回值
argType 参数类型
20.2 应用
20.2.1 function 作参数类型实现回调
void selectSort(int *p, int n, function<bool(int, int)> pCompare);
selectSort(arr,10,[](int x ,int y)
{return x>y;
});
20.2.1.2 function 作类成员实现回调
class A
{public:
A(const function<void()> & cb)
:_callback(cb)
{}
void notify()
{
_callback();
}
function<void()> _callback;
};A a(fct);
a.notify();
二十一、 std::bind
21.1 含义
bind 用来将可调用对象和参数一起进行绑定。可调用对象包括普通函数、全局函
数、静态函数、类静态函数甚至是类成员函数,参数包括普通参数和类成员。
语义
std::bind(funcName,argType,…);//绑定以存在的函数名,以及传入的实参
placeholders::_x 实参占位
placeholders::_1 表示第一个实参暂时不填实参数值,依次可推。
21.2 作用
21.2.1 绑定普通函数与参数及占位
double myDivide (double x, double y);auto fn_five = std::bind (myDivide, 10, 2);
cout << fn_five() << endl;
21.2.2 绑定对象与成员及占位
class MyPair2
{public: int add(int x, int y) {
return x + y; }
};auto bindObjfunc = bind(&MyPair2::add,mp2 , 2, 3);
21.2.3 函数重载情形
int add(int x, int y);
double add(double x, double y);auto bindGlobalFunc =
bind((int(*)(int,int))add,_1,_2);auto bindGlobalFunc2 =
bind(static_cast<double(*)(double,double)>(add),_1,_2);
注意
1.预先绑定的参数值通过 值传递进去;通过placehodler传递进去的 传递引用进去
2.bind 返回的是可调用的函数
3.绑定的参数 调用之前 需要确认是可用的
21.3 多态之 bind +fucntion
21.3.1.可实现多态使用
std::function<void(void)> f;
void foo();
void func(int a)f = std::bind(foo);
f();
f= std::bind(func,1);
f();
21.3.2.function 本是不可以包装类成员函数,bind实现类成员函数的绑定, 然后赋给 fucntion 对象,亦即实现了间接性的包装
std::function<void(void)> f;
class Foo
{public:
void method()
{
cout<<"Foo::void method()"<<endl;
}
void method2(string s)
{
cout<<"Foo:void method2()"<<endl;
}
};Foo foo;
f = std::bind(&Foo::method,&foo);
f();
f = std::bind(&Foo::method2,&foo,"china");
f();
二十二、 Unordered Contrainer 无序容器
22.1 unordered_map
采用hash_map 数据结构,无序
二十三、 Auto Memeory Manage 自动内存管理
23.1 RAII(Resource Acquisition Is Initialization)
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制
程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之
在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际 上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
①不需要显式地释放资源。 ②采用这种方式,对象所需的资源在其生命期内始终保持有效。
此时,所托管的资源,随对象的创建而获取,随对象的消失而消失,即所谓的 RAII 思想:资源获取即初始化。
原理上,是代理了被托管的对象指针,管理对象的生命周期,即实现自动释放。其 行为类似于所托管的对象指针,原因是,重载了 operator->和 operator*。 如下是智能指针的模板(template)化实现。
template <class T> class SmartPtr {public:explicit SmartPtr(T* pointee) : pointee_(pointee){}~SmartPtr() {delete pointee_;}T& operator*() const { //... return *pointee_;}T* operator->() const { //... return pointee_; } private:T* pointee_; //...
};
23.2 auto_ptr(deprecated)
代理new出来的对象,自动化释放对象,作用域处结束,释放对象。
存在问题
- 两个auto_ptr对象拥有同一个对象时,析构时都试图删除p,会出现崩溃问题
#include <iostream> #include <memory> using namespace std; int main() {int *p = new int(10); {auto_ptr<int> ap(p);cout<<*ap<<endl;}auto_ptr<int> ap2(p);cout<<*ap2<<endl;return 0;
}
- 作参数传递的时,亦是会出现同样的情况,两个指针,对同一段资源产生了引用行为。
23.3 unique_ptr
uniqu_ptr 的使用方法,基本上等同于 auto_ptr, 不同之处,就在于实现了资源的 唯一, 既不可以拷贝也不可以赋值,正如其名字一样。
- 解决了作为 参数拷贝时,无法拷贝和赋值的特性使 只能使用同一个对象,解决auto_ptr造成的释放崩溃
- 判断是否有资源
unique_ptr<Copy> up;
if(!up)
{cout<<"无资源托管"<<endl;
}
unique_ptr<Copy> up2(new Copy(10));
if(up2)
{cout<<"有资源托管"<<endl;}
- 允许托管左值 unique_ptr up2 =std::move(up); 相当于资源转移,被转移的资源不允许再使用
int main() { unique_ptr<Copy> up(new Copy(99));cout<<up.get()<<endl;unique_ptr<Copy> up2 =std::move(up);cout<<up2.get()<<endl;//up.get();//被转移资源 再使用 会造成崩溃
}
23.3.1 常用函数
- get() 返回所托管资源的指针
- release() 取消代理(放弃托管) ,返回资源句柄,注意:对象释放仍需手动维护
- reset() 重置 1.参数为空 释放之前的资源对象 2.参数为对象指针,先释放之前的资源对象,重新托管新的对象
- std::move() 允许托管左值 相当于资源转移,被转移的资源不允许再使用
23.4 shared_ptr
shared_ptr之间共享资源,共享同一个计数器,每次引用,引用计数+1
int main() { shared_ptr<int> sp(new int(10)); cout<<sp.get()<<endl;if(sp) {cout<<"有资源托管中"<<endl;} cout<<sp.use_count()<<endl;{shared_ptr<int> sp2 = sp; cout<<sp2.use_count()<<endl; }cout<<sp.use_count()<<endl;return 0;
}输出结果:
有资源托管中
1
2
1
比较:
- uinque_ptr 解决了 auto_ptr 中引用同一个对象的问题,方式就是不可引用同一个 对象。
- shared_ptr 解决了 auto_ptr 中引用同一个对象,共同拥有一个资源, 但不会重 析构的问题,原理是,在内部保持一个引用计数,并且仅当引用计数为 0 才能被删除, 不可以用数组。
都无法避免作用域重复析构崩溃
23.5 用法
- 基本类型计数测试
#include <iostream>#include <memory> using namespace std; void func(shared_ptr<int> sp) {// sp.reset(); //此处仅将自己所累积的计数减 1 cout<<sp.use_count()<<endl; sp.reset(); //此时 reset 等价于 sp 对象消失,若己为零,则不再减 1. }
int main() {
shared_ptr<int> sp(new int(10));
cout<<sp.get()<<endl;
if(sp){cout<<"有资源托管中"<<endl;
}
cout<<sp.use_count()<<endl;
shared_ptr<int> sp2 = sp;
cout<<sp2.use_count()<<endl;
cout<<sp.use_count()<<endl;
func(sp);
cout<<sp.use_count()<<endl;
return 0;
}
- 对象计数测试
reset 跟参数,会托管新对象,释放旧对象,如若不跟参数话,会将当前对象的引 用计数减 1
#include <iostream>
#include <memory>
using namespace std;
class A {public: A() { cout<<"A()"<<this<<endl; }
~A() { cout<<"~A()"<<this<<endl;
}
void dis() { cout<<"A::void dis()"<<endl;
}
};int main1() {
{ shared_ptr<A> sp(new A);sp.reset(new A()); cout<<"+++++++++++++++++++++"<<endl;
}cout<<"======================"<<endl;
}int main()
{ { shared_ptr<A> sp(new A); sp.reset();sp.reset();sp.reset(); cout<<"+++++++++++++++++++++"<<endl; }cout<<"======================"<<endl;
}
- 对象传参测试
#include <iostream>
#include <memory> using namespace std; class A {public: A() {cout<<"A()"<<this<<endl;
}~A() {
cout<<"~A()"<<this<<endl;
}void dis() { cout<<"A::void dis()"<<endl;
}
};void func(shared_ptr<A> &sp) //shared_ptr<A> &sp
{ cout<<sp.use_count()<<endl; sp.reset(); sp.reset(); cout<<"======================"<<endl;
}int main()
{ shared_ptr<A> sp(new A);cout<<sp.use_count()<<endl; func(sp); cout<<sp.use_count()<<endl; shared_ptr<A> sp2 = std::move(sp); //移动会将计数也一起移走 cout<<sp2.use_count()<<endl; cout<<sp.use_count()<<endl; //此时资源为 0 return 0;
}
对同一个对象,多次 reset 的结果,是仅对自己增加的计数减 1。要保证,当前的 对象的使用安全性。也不会对其它象的使用造成影响。
void func(shared_ptr<Copy> spc) //&spc 若传递的是引用,则引用计数不会加 1。离开函数也不会减 1
shared_ptr<A> sp2 = std::move(sp); //移动会将计数也一起移走
23.5.1 常用函数
- reset() 只可自杀 不可他杀
- std::move() 计数不会增加
- operator bool 判空
- operator= 支持赋值,复制
23.6 weak_ptr
#include <memory>
std::weak_ptr<int> wp1;
只能和 shared_ptr 类型指针搭配使用。甚至于,我们可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具。
借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息:
- 有多少指向相同的 shared_ptr 指针、
- shared_ptr 指针指向的堆内存是否已经被释放等等。
注意:
当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;
同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。
也就是说,weak_ptr 类型指针并不会影响所指堆内存空间的引用计数。
二十四、 Thread框架
24.1 join 与 detach
t.join 和 t.detach 标志着,线程对象和线程的关系。t.join 表识,线程与线程对象 的同步关系。而 t.detach 表识,线程与线程对象的异步关系。
join 是阻塞的。
注意:主线程结束后 detach() 可能会还没运行就销毁了
24.2 传参方式
线程,有自己独立的栈。可以共享全局的变量。在线程启动的时候可以传入启动参数。
1.传值
std::thread threadTest(func,arg1,arg2,…);
2.传引用
std::thread threadTest(func,std::ref(arg1),std::ref(arg2));
24.3 常用函数
join() //阻塞运行线程
joinable() // 线程是否阻塞的
detach() // 异步运行
sdt::ref() //引用化
24.4 同步之mutex
24.5 volatile
修饰变量,此变量可能被多线程访问和修改,加上此关键字,可以避免编译器优化,不使用存储在寄存器中的值,而是每次都去内存里去读。
24.6 lock(),unlock()
对某作用域加锁,解锁,但如果作用域抛异常可能会导致解锁失败,产生死锁。
可以控制加锁粒度。
24.7 try_lock(),unlock()
尝试加锁,加锁失败会返回false,
try_lock()
1.如果互斥锁当前未被任何线程锁定,则调用线程将其锁定(从此点开始,直到调用其成员解锁,该线程拥有互斥锁)。
2.如果互斥锁当前被另一个线程锁定,则该函数将失败并返回false,而不会阻塞(调用线程继续执行)。
3.如果互斥锁当前被调用此函数的同一线程锁定,则会产生死锁(具有未定义的行为)。 请参阅recursive_mutex以获取允许来自同一线程的多个锁的互斥锁类型。
24.8 std::lock_guard()
自动锁,声明范围内进行自动加锁解锁操作
在 lock_guard 对象构造时,传入的 Mutex 对象(即它所管理的 Mutex 对象) 会被当前线程锁住。在 lock_guard
对象被析构时,它所管理的 Mutex 对象会自动解 锁,由于不需要程序员手动调用 lock 和 unlock 对 Mutex
进行上锁和解锁操作, 因此这也是最简单安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex
对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常 处理代码。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void printEven(int i) {
if( i%2 == 0) cout<< i <<" is even"<<endl;
elsethrow logic_error("not even");
}void printThreadId(int id) { try{lock_guard<mutex> lck(mtx); //栈自旋 抛出异常时栈对象自我析构。 printEven(id);
// mtx.lock(); //printEven(id); //mtx.unlock(); }catch(logic_error & ){ cout<<"exception caught"<<endl; }
}int main() { thread ths[10]; //spawn 10 threads for(int i=0; i<10; i++) { ths[i] = thread(printThreadId,i+1); }for(auto & th: ths) th.join(); return 0;
}
24.9 死锁
死锁的原因是,container 试图多次去获取锁己获得的锁。
std::recursive_mutex 允 许多次获取相同的 mutex。
C++中 STL 中的容器,是非线程安全的。
24.10 std::recursive_mutex()
递归锁
调用线程从成功调用 lock 或 try_lock 开始占有recursive_mutex, 期间线程可以进行对 lock 或 try_lock的附加调用,所有权在线程调用 unlock 匹配次数时结束。
线程占有recursive_mutex时,若其他线程要求recursive_mutex所有权,调用lock将被阻塞,调用try_lock将返回false.
可锁定recursive_mutex的最大次数未指定的,但到达该数后,对 lock 的调用将抛出 std::system_error 而对 try_lock 的调用返回false;
若recursive_mutex在仍被线程占有时被销毁,则程序行为未定义。recursive_mutex满足 mutex 和 标准布局类型的所有要求。
24.11 同步之std::condition_variable
条件变量,多线程中对变量操作时对变量进行条件判断 从而当前线程需要是否阻塞或者被唤醒
条件变量(condition variable)是利用线程间共享的全局变量进行同步的一种机制,
主要包括两个动作:
一个线程等待某个条件为真,而将自己挂起;
另一个线程使的条件成立,并通知等待的线程继续。
为了防止竞争,条件变量的使用总是和一个互斥锁 结合在一起
C++11 中引入了条件变量,其相关内容均在<condition_variable>中。
这里主要 介绍 std::condition_variable 类。 条件变量 std::condition_variable 用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable 需要与 std::unique_lock 配合使用。 std::condition_variable 效果上相当于包装了 pthread 库中的 pthread_cond_*()系列 的函数。
当 std::condition_variable 对 象 的 某 个 wait 函 数 被 调 用 的 时 候 , 它 使 用 std::unique_lock(通过 std::mutex)来锁住当前线程。当前线程会一直被阻塞,直到另 外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒 当前线程。
24.11.1 成员函数
(1)、构造函数: 仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的。
(2)、wait: 当前线程调用 wait()后将被阻塞,直到另外某个线程调用 notify_*唤 醒当前线程;当线程被阻塞时,该函数会自动调用 std::mutex 的 unlock()释放锁,使
得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常 是另外某个线程调用
notify_*唤醒了当前线程),wait()函数也是自动调用 std::mutex 的 lock()。wait
分为无条件被阻塞和带条件的被阻塞两种。 无条件被阻塞:调用该函数前,当前线程应该已经对 unique_lock lck
完成了加锁。所有使用同一个条件变量的线程必须在 wait 函数中使用同一个 unique_lock。该 wait
函数内部会自动调用 lck.unlock()对互斥锁解锁, 使得其他被阻塞在互斥锁上的线程恢复执行。使用本函数被阻塞的当前线程在获得通知
(notified,通过别的线程调用 notify_*系列的函数)而被唤醒后,wait()函数恢复执行 并自动调用
lck.lock()对互斥锁加锁。
带条件的被阻塞: wait 函数设置了谓词(Predicate),只有当 pred 条件为 false 时 调用该 wait 函数才会阻塞当前线程,并且在收到其它线程的通知后只有当 pred 为 true 时才会被解除阻塞。因此,等效于 while
(!pred()) wait(lck)
(3)、wait_for: 与 wait()类似,只是 wait_for 可以指定一个时间段,在当前线程 收到通知或者指定的时间超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了 其它线程的通知,wait_for 返回,剩下的步骤和 wait
类似。
(4)、wait_until: 与 wait_for 类似,只是 wait_until 可以指定一个时间点,在当 前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。而一旦超时或 者收到了其它线程的通知,wait_until
返回,剩下的处理步骤和 wait 类似。
(5)、notify_all: 唤醒所有的 wait 线程,如果当前没有等待线程,则该函数什么也 不做。
(6)、notify_one: 唤醒某个 wait 线程,如果当前没有等待线程,则该函数什么也 不做;如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。
24.12 std::unique_lock()
独占锁,当前锁作用域结束之前,其余线程无法使用mutex。
std::unique_lock对象以独占所有权的方式(uniqueowership)管理mutex对象的上锁和解锁操作,即在unique_lock对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而unique_lock的生命周期结束之后,它所管理的锁对象会被解锁。
unique_lock具有lock_guard的所有功能,而且更为灵活。虽然二者的对象都不能复制,但是unique_lock可以移动(movable),因此用unique_lock管理互斥对象,可以作为函数的返回值,也可以放到STL的容器中。
24.13 std::atomic
c++11提供了原子类型std::atomic,理论上这个T可以是任意类型。
整形有这种原子变量已经足够方便,就不需要使用std::mutex来保护该变量啦。
struct OriginCounter { // 普通的计数器int count = 0;std::mutex mutex_;void add() {std::lock_guard<std::mutex> lock(mutex_);++count;}void sub() {std::lock_guard<std::mutex> lock(mutex_);--count;}int get() {std::lock_guard<std::mutex> lock(mutex_);return count;}
};struct NewCounter { // 使用原子变量的计数器std::atomic<int> count = 0;void add() {++count;// count.store(++count);这种方式也可以}void sub() {--count;// count.store(--count);}int get() {return count.load();}
};void main()
{NewCounter counter;std::thread thread1([&counter]() {int nTime = 10;while (nTime--){counter.add();printf("thread1:%d\n", counter.get());}});thread1.detach();std::thread thread2([&counter]() {int nTime = 10;while (nTime--){counter.add();printf("thread2:%d\n", counter.get());}});thread2.join();return;
}
thread1:1
thread1:2
thread1:4
thread1:5
thread1:6
thread1:7
thread1:8
thread1:9
thread1:10
thread1:11
thread2:3
thread2:12
thread2:13
thread2:14
thread2:15
thread2:16
thread2:17
thread2:18
thread2:19
thread2:20
24.14 std::call_once
c++11提供了std::call_once来保证某一函数在多线程环境中只调用一次,它需要配合std::once_flag使用。
类 std::once_flag 是 std::call_once 的辅助类。
传递给多个 std::call_once 调用的 std::once_flag 对象允许那些调用彼此协调,从而只令调用之一实际运行完成。
std::once_flag 既不可复制亦不可移动。
总之避免,某函数被多个对象调用,多个对象调用std::call_once时传入std::once_flag来判断该对象是否能调用目标函数。
#include <thread>
#include <mutex>
#include <iostream>std::once_flag onceflag;void CallOnce() {std::call_once(onceflag, []() {std::cout << "call once" << std::endl;});
}int main() {std::thread threads[5];for (int i = 0; i < 5; ++i) {threads[i] = std::thread(CallOnce);}for (auto& th : threads) {th.join();}return 0;
}
call once
24.15 volatile相关
volatile修饰过的变量,编译器对访问该变量的代码通常不再进行优化。
24.16 异步相关
std::future
std::future用于访问异步操作的结果,从翻译上来讲就是将来的值,future.get()时会阻塞,如果有值,就立刻返回值;如果没有,则阻塞当前线程当前位置,直到有值,也就是所谓的异步;
std::promise
std::promise内部有个future 类似
class Promise{public:Future* m_pFuture;
}
std::promise 主要用来传递future包含的信息的 包括信息的set_value()、get_future()
当需要获取线程中的某个值,可以使用std::promise
std::packaged_task
当需要获取线程函数返回值,可以使用std::packaged_task。
std::packaged_task: (1).禁用拷贝赋值。(2).支持移动赋值。
std::promise与std::future配合使用
#include <functional>
#include <future>
#include <iostream>
#include <thread>
#include <windows.h>int main() {std::promise<int> prom;std::future<int> fut = prom.get_future();std::thread thread1([&fut]() {int startCount = GetTickCount();printf("thread1 start :%d\n", startCount);int x = fut.get();std::cout << "value: " << x << std::endl;int endCount = GetTickCount();printf("thread1 end :%d\n", endCount);});thread1.detach();std::thread thread2([&prom]() {int value = 144;printf("thread2 set_value :%d\n", value);prom.set_value(value);});thread2.join();system("pause");return 0;
}
thread1 start :32191656
thread2 set_value :144
value: 144
thread1 end :32191656
留意输出结果会发现 有一个输出顺序,thread1 先等待一会,等待thread2set_value之后thread1才继续输出。
就是get时没有获得到值所以阻塞中的过程。
std::packaged_task与std::future配合使用
#include <functional>
#include <future>
#include <iostream>
#include <thread>using namespace std;int main() {std::packaged_task<int(int)> task([](int in)->int {return in + 1;});std::future<int> fut = task.get_future();std::thread(std::move(task), 5).detach();//move是因为std::packaged_task不支持拷贝构造,支持移动构造。cout << "result " << fut.get() << endl;return 0;
}
result 6
std::future用于访问异步操作的结果,
而std::promise和std::packaged_task在future高一层,它们内部都有一个future,promise包装的是一个值,packaged_task包装的是一个函数,当需要获取线程中的某个值,可以使用std::promise,当需要获取线程函数返回值,可以使用std::packaged_task。
24.17 std::async
async是比future,packaged_task,promise更高级的东西,
它是基于任务的异步操作,通过async可以直接创建异步的任务,返回的结果会保存在future中,
不需要像packaged_task和promise那么麻烦,关于线程操作应该优先使用async。
#include <functional>
#include <future>
#include <iostream>
#include <thread>
#include <Windows.h>using namespace std;int main() {auto res = std::async(std::launch::async,[](int in)->int {int start = GetTickCount();printf("func start %d\n", start);Sleep(2000);int end = GetTickCount();int n = in + 1;printf("func value %d\n", n);printf("func end %d\n", end);return n;}, 5);// res.wait();int beforeTime1 = GetTickCount();printf("Main Thread beforeTime %d\n", beforeTime1);cout << "Get Value:"<<res.get() << endl; // 阻塞直到函数返回int afterTime1 = GetTickCount();printf("Main Thread afterTime1 %d\n", afterTime1);return 0;
}
Main Thread beforeTime 34517000
func start 34517000
func value 6
func end 34519015
Get Value:6
Main Thread afterTime1 34519015
语法
std::future<T> std::async(std::launch::async | std::launch::deferred, func, args...);
std::future< T >:返回参数
参数1 执行策略:
- std::launch::async表示任务执行在另一线程
- std::launch::deferred表示延迟执行任务,调用get或者wait时才会执行,不会创建线程,惰性执行在当前线程。
如果不明确指定创建策略,以上两个都不是async的默认策略,而是未定义,它是一个基于任务的程序设计,内部有一个调度器(线程池),会根据实际情况决定采用哪种策略。
参数2 func:即将执行的函数
参数args…: 函数实参
[学习][记录] c++语言:从放弃到入门 <一> c++11新关键字以及引入的新特性相关推荐
- 1C语言 从放弃到入门-王桂林-专题视频课程
<1>C语言 从放弃到入门-1052人已学习 课程介绍 C语言 从放弃到入门 课程收益 所有对C语言有入门恐惧的人. 讲师介绍 王桂林 更多讲师课程 ...
- Python语言学习:python语言的特点、入门、基础用法之详细攻略
Python语言学习:python语言的特点.入门.基础用法之详细攻略 相关内容 Python 基础教程 目录 python语言的特点 python语言的入门 python语言的基础用法 python ...
- Git学习记录 力做全网最强入门教程
目录 Git学习记录 力做全网最强入门教程 什么是GitHub? 什么是Git? Git的配置 Git的安装(只介绍windos操作系统下) Git的配置 至此我们的入门教程到此结束,更新中级教程要等 ...
- 2023.2.3,周五【图神经网络 学习记录17】二部图——BiNE算法:显式关系,隐式关系;新的随机游走方式 特点:随机游走次数 是跟节点中心性相关的,在随机游走的过程中 添加一个停止随机游走的概率
声明:仅学习使用~ 前情提要: 2023.2.2,周四[图神经网络 学习记录16]异构图Graph Embedding算法--GATNE(异构图多属性 多边 类型算法),不建议普通PC跑-PyChar ...
- 第031讲:永久存储,腌制一缸美味的泡菜 | 学习记录(小甲鱼零基础入门学习Python)
(标答出处: 鱼C论坛) <零基础入门学习Python> 测试题: 0.pickle的实质是什么? pickle的实质是利用一些算法,将你的数据对象腌制成二进制文件,存储在磁盘上,也可以放 ...
- 第063讲: 论一只爬虫的自我修养11:Scrapy框架之初窥门径 | 学习记录(小甲鱼零基础入门学习Python)
上一节课我们好不容易装好了 Scrapy,今天我们就来学习如何用好它,有些同学可能会有些疑惑,既然我们懂得了Python编写爬虫的技巧,那要这个所谓的爬虫框架又有什么用呢?其实啊,你懂得Python写 ...
- 第023、024讲:这帮小兔崽子汉诺塔 | 学习记录(小甲鱼零基础入门学习Python)
(标答出处: 鱼C论坛) <零基础入门学习Python> 测试题: 0.使用递归编写一个十进制转换为二进制的函数(要求采用'取2取余'的方式,结果与调用bin()函数一样返回字符串形式) ...
- 第019讲:我的地盘听我的 | 学习记录(小甲鱼零基础入门学习Python)
(标答出处: 鱼C论坛) <零基础入门学习Python> 测试题: 0.下边程序会输入什么? def next():print('我在next()函数里...')pre()def pre( ...
- 量化交易学习记录——(一)全局了解|入门
本人是计算机的,完全的金融小白,所以这里整理的比较基础. 1. 找专业的网站/论坛 首先要了解哪里是专业人士聚集的地方,论坛和网站以及github整理如下: github项目:https://gith ...
- 学习记录_C语言打砖块小游戏
第一次体验到了写代码一上午,修BUG修一天的感觉.但还有一个BUG始终不知道如何解决,那便是如果额外产生的小球触碰到礼盒砖块(会产生额外球),便会在左上角出现隐形球.无奈只好将礼盒砖块改为一个,并将存 ...
最新文章
- 046_CSS3动画
- flex----导航
- elasticsearch id查询_互联网公司中对【Elasticsearch】的真实应用案例
- 二叉树题目----5 平衡二叉树 AND 根据二叉树创建字符串
- Linux 内核打印级别
- [转载]url带中文参数显示乱码的问题
- 超详细深度学习debug指南,国外小哥手把手教你如何调试模型 | 附PPT
- 网御神州和北京邮电大学成立信息安全联合实验室(2007-07-04)
- JQuery杂项方法
- drools规则引擎使用文档
- js图片添加文字水印并设置水印居中的方法
- word页眉页脚设置
- Accuracy和Precision的区别
- 厦门大学计算机保研学校,厦门大学计算机科学系(专业学位)计算机技术保研夏令营...
- 排版——用Markdown写Csdn博客
- html显示当前小时分钟秒秋,北语18秋《网页制作》作业1234
- “由于没有公钥,无法验证下列签名”解决办法
- 网页文字涉及侵权怎么处理
- 一文看懂5种ESD防护方法!
- 想成为优秀的程序员这些码德不能缺
热门文章
- 成功解决 遇到3dmax模型文件出错,找回并修复出错文件
- c语言斐波那契数列for循环数组,C语言斐波那契数列的四种实现方式—递归,迭代,数组,队列...
- cass有坐标文件生成里程文件_南方CASS里程文件生成
- daemontools的安装、示例、简介
- html中内部css样式怎么写,HTMLCSS基础-内联样式和内部样式表
- windows操作系统启动后,不用输入用户名和密码,自动登录设置
- 全球国家或地区 及其 区号
- C# sqlsugar依赖引用报错的问题解决
- 搜集的一些电压跟随器的问题和回答
- ensp VLAN划分