一、初始化
1、列表初始化{}(单一类型)
多参数的构造函数的隐式类型转换,之前只支持单参数的。
string ret = "xxxxxx" (explicit可以阻止这种隐式转换)
2、initializer_list(类型转换)
底层为两个指针,一个指向第一个元素,另一个指向最后一个元素的后一个位置
initializer_list是类模板
让vector支持lt构造。
vector(initializer_list<T> lt)
{
reserve(lt.size());
for (auto e : lt)
{
push_back(e);
}
}
二、声明
1、decltype
2、auto
3、nullptr
void func(int* p)
{}
void func(int p)
{}
//const enum inline去替代宏
int main()
{
int* p = NULL; // int* p = 0;
func(NULL); // func(0);
return 0;
}
三、final/override
四、STL新增容器
1、array
int main()
{
int a1[10];
array<int, 10> a2;
a1[15] = 1; // 指针的解引用
a2[15] = 1; // operator[]函数调用,内部检查
// 用这个也可以,显得array很鸡肋
vector<int> a3(10, 0);
}
2、forward_list
单链表
五、左值/右值
1、如何区分左值/右值
左值:是一个表示数据的表达式(如变量名或解引用的指针),我们
可以获取它的地址
,一般可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
右值:也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,
右值不能取地址
。右值引用就是对右值的引用,给右值取别名。
2、左值引用/右值引用
交叉使用
使用场景和价值
左值引用的使用场景和价值是什么?
使用场景:1、做参数 2、做返回值 价值->减少拷贝
右值引用的使用场景和价值是什么?
使用场景:某些情况下可以减少深拷贝
场景1:自定义类型中深拷贝的类,且必须传值返回的场景
右值引用如何减少深拷贝?
如何用右值引用进行优化?
一个深拷贝场景:
用右值引用接收 右值将亡值(自定义类型),内置类型为纯右值:
s1:构造+拷贝构造+赋值 --> 构造+移动构造 2次深拷贝-->0次
s2:构造+拷贝构造+拷贝构造-->构造+移动构造(str是一个左值,为了优化为移动赋值,编译器内部将其识别为右值将亡值) 2次深拷贝-->0次(下图为s2的场景)
场景2:容器的插入接口,如果插入的对象是右值,可以利用移动构造将资源转移给数据结构中的对象,从而减少拷贝,提高效率。
push_back(val) new Node(val) _val(val)
都要提供一个&&右值引用的版本。
六、万能引用/完美转发
对于T&&接收后的t,无论是否折叠,即无论是左值引用&还是右值引用&&,其本身的属性是左值
当需要传递右值时,需要用forward<T>(t)使其保持右值属性。
能传给swap中的左值引用,说明移动构造时s变量本身就为左值属性。
移动拷贝时需要swap完成资源转移,必须要修改,则右值引用本身为左值属性。
利用forward,逐层传递,保持右值属性。