(目录占位)
1. 前言:
C++ 11 是在 C++ 98 之后又一个变化比较大的标准。为C++增加了很多东西,其中有一部分是有用的,有一部分是我自认为作用不是很大东西。这一章呢?我们就来说说C++11我,我认为对性能优化最有用的一部分 ---- 右值引用
2. 简单回顾:左值引用
左值?我们现在说说什么是左值?
直接抛结论:能取地址的值或者表达式结果就是左值。左值可以出现在 = 左边,也可以出现在 = 右边,不能仅仅以 = 号来区分左右值。
最开始时候的引用,就是左值引用,在C++的语法层面上,引用是给一个变量起一个别名,是不开辟空间的,目的是为了:减少拷贝,提高效率。
我们通常在学习的时候与 指针 对照学习。
语法层面(指针对比引用):
引用在定义的时候必须初始化,指针在定义的时候是可以不初始化的。
引用是没有空引用的,但是指针呢?他是有空指针的,比如C++11之前的NULL和C++11提供的nullptr。
引用是不开辟空间的,但是指针是需要开辟4个字节或者8个字节的空间的。
引用和指针都有一个概念:权限的方法和缩小,首先一说,只有引用和指针有权限的放大和缩小。其他的语法是没有的,我们在学习的时候,千万不要自己创造语法...
权限的放大:
#include <iostream>int main()
{const int a = 10;int &b = a; // 权限的放大const int c = 20;int *p = &c; // 权限的放大return 0;
}
变量 a 的权限是 可读, 但是 变量 b 作为变量 a 的引用,权限却是可读可写。
变量 c 的权限是 可读, 但是 变量 p 指向 变量 a,权限却是可读可写。
上面这两种情况都是编译器默认不被允许的。
权限的缩小:
#include <iostream>int main()
{int a = 10;const int &b = a; // 权限的缩小int c = 20;const int *p = &c; // 权限的缩小return 0;
}
变量 a 的权限是 可读可写, 但是 变量 b 作为变量 a 的引用,权限是可读。
变量 c 的权限是 可读可写, 但是 变量 p 指向 变量 c,权限是可读。
上面的这两种情况是编译器默认被允许的。
小结:
权限的放大是不被允许的,但是权限的缩小是被允许的。
权限的放大和缩小都是语法层面的概念,都是被编译器约束的,我们可以通过强制类型转换来放大或者缩小权限(但是不推荐,因为有风险),都只是为了“骗”过编译器。
汇编层面:
引用和指针是一个的,都是开辟可一块空间,用来保存地址,指针变量就是用来保存指向的对象的地址,引用也是一样的,开辟一块空间,用来保存被起别名的对象的地址。
3. 本章主角:右值引用
相对于左值来说,右值就是不能取地址的值,并且右值是不能出现在 = 号的左边的。
自定义类型的右值常见的两种形式 : 临时对象 匿名对象
C++11 对右值又做了细分:纯右值(内置类型) 和将亡值(自定义类型),我们的主要研究对象就是:将亡值(自定义类型)
编译器在进化过程中,将 拷贝构造 + 构造 优化成 直接构造。
比如说在函数栈帧内返回一个比较大的对象的时候,右值引用的价值就极大了。
{
原来: 跨行的:构造 + 拷贝构造
现在: 跨行的:构造 + 移动构造
}
减少了一次拷贝,还是很可观的。
移动构造,移动赋值
有了移动构造之后: 返回时先拷贝构造生成临时对象,用临时对象移动构造
如果 “析构函数”,“拷贝构造”,“拷贝赋值” 都没有实现编译器就会自动生成 “移动构造”
编译器生成的移动构造:内置类型去值拷贝,自定义类型去调用它自己的移动构造
为什么?
需要显式写析构,说明有资源要释放,说明需要显式写拷贝构造和重载
说明要显式写移动构造和移动赋值
要么都自己写,要么都编译器自己生成。
4. 左值引用和右值引用
这个部分我们主要讨论一下,左值引用是否能引用右值,右值引用是否能引用左值,就是是否能交叉?
先看右值引用能否引用左值?
但是这样:const 左值引用 可以给引用右值 是可以的
除了这样,我们还可以通过强制类型转换来让左值引用引用右值,右值引用引用左值。
5. 小结
左值引用和右值引用的目的都是为了:减少拷贝,提高效率。但是他们都是编译器层面的概念,是可以通过强制类型转化来转化的(骗编译器,不建议)。