目录
4.单参数类型转换可以不用{ }直接等号赋值,也是列表初始化。
列表初始化({ }初始化)
C++11以前,只有数组和结构体(没有用户定义的构造函数、没有私有或保护的非静态数据成员、没有基类、没有虚函数的)支持{ }初始化;C++11之后,几乎一切对象都可使用{ }初始化。
使用一个日期类来演示
1.对于自定义类型来说
本质是类型转换,中间产生临时对象,不过优化后是直接构造。
将2025,3,27按照Date类的构造函数来产生临时对象,然后赋给d1对象,优化后就成了直接调用Date类的构造函数构造d1对象。
2.使用{ }初始化时,可以省略赋值符号=。
3.C++11内置类型也支持{ }初始化。
4.单参数类型转换可以不用{ }直接等号赋值,也是列表初始化。
5.initialize_list
上面的初始化很方便,但对于对象容器的初始化还不太方便,比如一个vector对象,想用N个不同值构造初始化,就要去实现对应的构造函数。
C++11中实现了一个std::initialize_list的类,这个类的本质是开一个数组,将数据拷贝到数组中,然后进行对象的构造,支持迭代器遍历,但数据的类型要相同。
it 和 itt 会指向同一块临时数组空间。
在语法识别时,如果没有匹配的构造函数,就不能进行隐式类型转换,就会按照initialize_list处理
右值引用与移动语义
左值引用or右值引用
左值
可以获取地址的,可出现在赋值符号=的左边或者右边的一个表示数据的表达式(如变量名、解引用的指针等)。
对左值的引用就是左值引用。
右值
不可以获取地址的,不可以修改的,只能出现在赋值符号=的右边的表示数据的表达式(如匿名对象、表达式结果产生的临时对象、函数返回的临时对象、字符常量等)
对右值的引用就是右值引用。
核心区别就是能否取地址。底层都是指针
相互引用关系
1.左值引用不能直接引用右值,但const左值可以引用右值。
2.右值引用不能直接引用左值,但右值引用可以引用move左值(move是进行类型强转的一个函数)。
3.右值引用后值就可以修改了,因为右值引用算一个变量表达式,变量表达式是左值,可以修改。
引用延长生命周期
临时对象和匿名对象生命周期只在当前行,引用可以延长到与引用生命周期相同(右值引用或const左值引用)。
参数匹配
函数传参时,实参为左值会匹配左值引用,const左值匹配const左值引用,右值匹配右值引用。
移动构造和移动赋值
移动构造函数是一种构造函数,类似拷贝构造函数,不过要求第一个参数是该类类型的右值引用,而且如果有额外参数,额外参数必须有缺省值。
移动赋值是一个赋值运算符的重载,和拷贝赋值构成重载,类似拷贝赋值函数,不过要求第一个参数是该类类型的右值引用。
移动构造和移动赋值的本质是移动资源,是“抢走”引用的右值对象的资源来提高效率,而不是像拷贝构造和拷贝赋值那样拷贝资源,对于包含深拷贝的成员变量的类才有意义。
右值引用和移动语义在传参中的提效
如果实参是一个左值,容器内部继续调用拷贝构造进行拷贝。
如果实参是一个右值,容器内部则调用移动构造,直接将右值对象的资源转移到容器空间的对象上,因为实参为右值时,一般都是生命周期要结束的变量或者常量,所以直接将其资源转移是没有问题的。
lambda表达式(匿名函数对象)
特点
1. 和普通函数不同的是它是可以定义在函数内部的“函数”。
2.lambda表达式语法对于使用层而言没有类型,所以我们一般用auto或者模版参数定义的对象去接收lambda对象。
3.lambda表达式格式:[capture - list] (parameters) -> return type {function boby}
[capture - list]:捕捉列表,要出现在lambda表达式的开始位置,编译器根据[ ]来判断接下来的代码是否为lambda函数,捕捉列表能捕捉上下文中的变量供lambda函数使用,可以传值和传引用捕捉,如果捕捉列表为空,也不可以省略。
(parameters) :参数列表,与普通函数的参数列表相似,如果参数列表为空,可以全部省略。
-> return type :返回值类型,用追踪返回类型的形式来声明函数返回值类型,没有返回值可全部省略。不过一般返回值类型明确,也可省略,让编译器自动推导。
{function boby}:函数体,和普通函数类似,可以使用参数和捕捉到的变量,不可省略。
极简版:
正常版:
实用场景
例:当需要在不同位置比较两个对象的不同数据来实现排序等操作时。
一个学生类的vector数组,如果又需要按身高排升序、降序,又需要按体重排升序、降序,仿函数实现不太好弄,但只需要写一个lambda表达式即可。
捕捉列表
lambda表达式中默认只能使用lambda函数体和参数中的变量,想使用外层作用域中的变量就需要进行捕捉。
注:1.局部的静态变量和全局变量是不需要捕捉就可以使用的,也不能捕捉。
2.捕捉的变量默认为const,传值捕捉的变量不可修改,传引用捕捉的变量可以修改,但修改会影响到外层作用域中的变量值。
捕捉方式
1.显示传值和传引用捕捉
[a,b,&c] &c为引用,不是取地址。
2.隐式值/引用捕捉
[=] 隐式值捕捉(不可修改):在lambda表达式中用了哪些变量,就在外层作用域中捕捉哪些变量。
[&] 隐式引用捕捉(可修改):在lambda表达式中用了哪些变量,就在外层作用域中捕捉哪些变量。
3.混合捕捉
[&,a,b] a、b为传值捕捉,其余全为引用捕捉
[=,&a,&b] a、b为传引用捕捉,其余全为值捕捉
注:&和= 符号只能放在开头。
mutable
传值捕捉相当于用const修饰拷贝来的值,mutable可以去掉const属性,让其可以修改,但修改不会影响外部作用域中的值,因为是拷贝。
小知识
1.\ddd d为数字,表示1到3个八进制的数字, \xdd d为数字,表示两个十六进制的数字。
2.sizeof中的表达式不会计算
例:short a = 4; int b = 3;
cout<<sizeof(a=b+1) <<' ';
cout<<a;
打印结果为2 4