建设网站服务器是什么,技术支持 骏域网站建设专家佛山,织梦后台如何做网站地图,琳琅秀网站建设目录
前言
列表初始化
std::initializer_list
右值引用和移动拷贝
左值和右值
左值引用和右值引用的区别
万能引用#xff08;引用折叠#xff09;
完美转发
默认成员函数控制 列表初始化
在C98中#xff0c;标准允许使用花括号{}对数组或者结构体元素进行统一的列…目录
前言
列表初始化
std::initializer_list
右值引用和移动拷贝
左值和右值
左值引用和右值引用的区别
万能引用引用折叠
完美转发
默认成员函数控制 列表初始化
在C98中标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如
//也算是兼容C
struct Point
{int _x;int _y;
};
int main()
{int array1[] { 1, 2, 3, 4, 5 };int array2[5] { 0 };Point p { 1, 2 };return 0;
}C11扩大了{ }(初始化列表)的使用范围使其可用于所有的内置类型和用户自定义的类型使用初始化列表时可添加等号()也可不添加。
struct Point
{int _x;int _y;
};
int main()
{int x1 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// C11中列表初始化也可以适用于new表达式中int* pa new int[4] { 0 };return 0;
}创建对象时也可以使用列表初始化方式调用构造函数初始化
class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout Date(int year, int month, int day) endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2022, 1, 1); // old style// C11支持的列表初始化下面两个会调用构造函数初始化Date d2{ 2022, 1, 2 };Date d3 { 2022, 1, 3 };return 0;
}std::initializer_list
initializer_list是C11新增加的容器底层就是一个数组。
int main()
{//这里初始化其实是构造初始化先不在意底层initializer_listint l {0, 1, 2, 34};initializer_listint::iterator it l.begin();while (it ! l.end()){cout *it ;it;}cout endl;
}initializer_list平常并无作用但是C11对{}有特殊处理它真正的应用场景是用于构造其它容器
//C11为vector、map等容器都提供了initializer_list做参数的构造
//原理也很简单先将{}转变成initializer_list,然后遍历initializer_list尾插即可
int main()
{vectorint v { 0, 1, 2, 3, 4, 5 };for (auto e : v) cout e ;cout endl;mapint, int m { {0, 1}, {1, 0}, {10, 9} };for (auto kv : m){cout kv.first : kv.second endl;}cout endl;
}右值引用和移动拷贝
左值和右值
首先先分清什么是左值什么是右值——不是在左边的就叫左值在右边的就叫右值
左值可以出现赋值符号的左边右值不能出现在赋值符号左边。
最显著的特点就是左值可以被取地址右值不能被取地址是否真的存储
左值引用就是给左值的引用给左值取别名。
// 以下的p、b、c、*p都是左值
int* p new int(0);
int b 1;
const int c 2;
// 以下几个是对上面左值的左值引用
int* rp p;
int rb b;
const int rc c;
int pvalue *p;右值不能被取地址例如字面常量、表达式返回值函数返回值(左值引用返回不是右值传值返回才是右值)
右值引用就是对右值的引用给右值取别名
// 以下几个都是常见的右值
10;
x y;
fmin(x, y);
// 以下几个都是对右值的右值引用
int rr1 10;
double rr2 x y;
double rr3 fmin(x, y);左值引用和右值引用的区别 1.左值引用只能引用左值不能引用右值右值引用同理 2.const 左值引用可以引用左值也可以引用右值但是反过来就不行 3.右值引用可以引用move后的左值 左值引用的使用场景 做参数和做返回值都可以提高效率
为什么要有右值引用呢主要是为了弥补左值引用的不足
场景当函数返回对象是一个局部变量出了函数作用域就不存在了就不能使用左值引用返回 只能传值返回。
例如zzb::string to_string(int value)函数中可以看到这里只能使用传值返回传引用返回当该函数栈帧被销毁的时候该位置有可能被其他变量所占用存在很大的问题但是传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)效率很低
zzb::string to_string(int x){bit::string ret;while (x){int val x % 10;x / 10;ret (0 val);}
内置类型的右值叫做纯右值将自定义的右值称为将亡值
我们先来看下面这两种情况
int main()
{bit::string s1 bit::to_string(1234);bit::string s2;s2 bit::to_string(2345);return 0;
} 下面的那个 str拷贝给临时对象临时对象拷贝构造给ret2 自定义创建时调用一次构造函数 函数栈帧销毁的时候创建临时对象的时候调用拷贝构造 返回值作为右值给左值赋值的时候又调用拷贝构造 上面的减少的一次可以理解为没有产生临时对象了直接赋值给ret2这时编译器优化后的结果
老一点的编译器可能没有这样优化 如果是浅拷贝的类那还没事但如果是深拷贝的类短短一个赋值操作就要深拷贝三次代价太大了反正这个函数栈帧里的空间都要被销毁的如果把它拿过来直接用的话是不是就很方便了
所以就有了移动构造和移动赋值这时候右值引用的价值就体现出来了可以区分左值和右值了
如果参数传的是将亡值则直接将资源交换不仅减少了拷贝还将不要的资源转移到了即将要销毁的空间过后自动销毁一举两得
移动构造——不用开辟空间直接交换得到目标值目标即将被销毁的时候使用 右值引用是间接起作用的对深拷贝的类有意义 左值引用是直接起作用传引用返回 右值被右值引用后的属性是左值
右值不能修改但是被右值引用之后需要被修改——属性变成左值不能修改怎么转移资源也就无法实现移动构造和移动拷贝
万能引用引用折叠 函数模板下才有用 可以接受左值和右值左值的时候就相当于将两个折叠成一个所以也叫引用折叠
完美转发
按照上面的写法写即可
可以保持原有属性右值被右值引用后属性变成左值 默认成员函数控制 这里为什么条件那么严格呢
其实这三个一般都是绑定在一起的因为写了析构一般都设计深拷贝所以也就要自己写拷贝构造和拷贝赋值不然会出现问题