Open lunix555 opened 2 years ago
关于C++11新特性,最先提到的肯定是类型推导,C++11引入了auto和decltype关键字,使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。
auto:让编译器在编译器就推导出变量的类型,可以通过=右边的类型推导出变量的类型。
auto a = 10; // 10是int型,可以自动推导出a是int decltype:相对于auto用于推导变量类型,而decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算。
cont int &i = 1; int a = 2; decltype(i) b = 2; // b是const int&
众所周知C++11新增了右值引用,这里涉及到很多概念:
左值:可以取地址并且有名字的东西就是左值。
右值:不能取地址的没有名字的东西就是右值。
纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。
将亡值:可以理解为即将要销毁的值。
左值引用:对左值进行引用的类型。
右值引用:对右值进行引用的类型。
移动语义(std::move):转移资源所有权,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用。
完美转发(std:forward):可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。
返回值优化:当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。
C++98中{}的初始化问题 在C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定。
int array1[] = {1,2,3,4,5}; int array2[5] = {0}; 对于一些自定义的类型,却无法使用这样的初始化。
vector
在C++11中可以直接在变量名后面加上初始化列表来进行对象的初始化。
struct A { public: A(int) {} private: A(const A&) {} }; int main() { A a(123); A b = 123; // error A c = { 123 }; A d{123}; // c++11
int e = {123}; int f{123}; // c++11
return 0; }
列表初始化的好处
方便,且基本上可以替代括号初始化
可以使用初始化列表接受任意长度
可以防止类型窄化,避免精度丢失的隐式类型转换
什么是类型窄化,列表初始化通过禁止下列转换,对隐式转化加以限制:
从浮点类型到整数类型的转换
从 long double 到 double 或 float 的转换,以及从 double 到 float 的转换,除非源是常量表达式且不发生溢出
从整数类型到浮点类型的转换,除非源是其值能完全存储于目标类型的常量表达式
从整数或无作用域枚举类型到不能表示原类型所有值的整数类型的转换,除非源是其值能完全存储于目标类型的常量表达式
模板的改进 C++11关于模板有一些细节的改进:
C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误。
int main() {
std::vector<std::vector
C++11引入了using,可以轻松的定义别名,而不是使用繁琐的typedef。
typedef std::vector<std::vector
C++11之前只有类模板支持默认模板参数,函数模板是不支持默认模板参数的,C++11后都支持。
template <typename T, typename U=int>
class A {
T value;
};
template
};
类模板的默认模板参数必须从右往左定义,而函数模板则没有这个限制。
template <typename R, typename U=int> R func1(U val) { return val; }
template
int main() {
cout << func1<int, double>(99.9) << endl; // 99
cout << func1<double, double>(99.9) << endl; // 99.9
cout << func1
c++11关于并发引入了好多好东西,有:
std::thread相关
std::mutex相关
std::lock相关
std::atomic相关
std::call_once相关
volatile相关
std::condition_variable相关
std::future相关
async相关
取地址的、有名字的就是左值。左值的生存期长,可以作为赋值的对象。 右值指临时对象,只在当前语句有效,右值又可以细分为纯右值、将亡值。纯右值指的是临时变量和不跟对象关联的字面量值;将亡值则是 C++11 新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用)。如std::move 的返回值。将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值
std::move()将左值强制转换为右值。
使用一个全局变量进行标志
std::function 是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。 定义格式:std::function<函数类型>。 std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。它比普通函数指针更加的灵活和便利。
可将std::bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。 std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。 概括:设置可调用对象(函数)的某些参数为可变变量,将某些参数设置为固定值,然后将整个调用对象返回成std::funciton类型
array为在栈空间内申请的数组,逻辑上和物理上都是连续的,且因为栈空间的特性访问、复制效率较高,可以直接将一个array数组复制到另一个array数组。 对比之下,vector是在堆空间申请的内存,在逻辑上连续,物理上不连续,所以在复制的时候只能分各个元素赋值(用new和malloc申请的空间为非连续空间,具体的原因和windows页式分配空间有联系)。
为一个匿名函数,支持在函数体内声明,其形式一般为: [ capture list ] ( parameter list) -> return type { function body } capture list,捕获列表,是一个lambda所在函数中定义的局部变量的列表。lambda函数体中可以使用这些局部变量。捕获可以分为按值捕获和按引用捕获。非局部变量,如静态变量、全局变量等可以不经捕获,直接使用;
parameter list,参数列表。从C++14开始,支持默认参数,并且参数列表中如果使用auto的话,该lambda称为泛化lambda(generic lambda);
return type,返回类型,这里使用了返回值类型尾序语法(trailing return type synax)。可以省略,这种情况下根据lambda函数体中的return语句推断出返回类型,就像普通函数使用decltype(auto)推导返回值类型一样;如果函数体中没有return,则返回类型为void。
function body,与任何普通函数一样,表示函数体。
Lambda表达式可以忽略参数列表和返回类型,但必须包含捕获列表和函数体。
auto f = [] { return 42; }
cout << f() << endl;
默认情况下,对于一个按值捕获的变量,lambda不能改变其值。如果希望能改变这个被捕获的变量的值,就必须在参数列表之后加上关键字mutable,因此,可变lambda不能省略参数列表:
int v1 = 42;
auto f=[v1] () mutable {return ++v1;};
v1=0;
auto j = f(); //j is 43
一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const类型还是一个非const类型:
int v1 = 42;
auto f=[&v1] () {return ++v1;};
v1=0;
auto j = f(); //j is 1