Ubuntu 18.04 gcc 4.8.4
什么是引用
在代码层面简单的理解就是变量的别名,简单的讲我们可以通过以下方式声明一个引用:
int val = 10;
int& val_ref = a;
但是,如果这样引用
int &a = 10;
这样会报错,因为u对于左值引用来说,需要右边的临时变量可以取地址,而对于常量10来说时无法取地址的。
与这个概念经常被一起提及的便是指针,那么这两者究竟有什么关系呢?
引用的作用
无论是指针还是引用,其两者有一个重要的作用:减少值拷贝。回忆以下我们在学习c/c++的时候,在定义函数会存在的三种传递方式:值传递、指针传递、引用传递。三者中值传递会发生值拷贝影响程序的运行效率,此外在处理函数返回值的时候也可能会发生值拷贝。设想一下这种情况:当我们需要传递或者返回一个很大很复杂的数据结构的时候,值拷贝的存在是十分消耗资源的。
引用占用内存吗?
从表面意思上说,引用是变量的别名,可能我们就认为其不占用内存,而你进行计算几个引用的大小时,返回的是其变量的大小,那么其究竟是否占用内存呢? 我们通过代码探究一下:
#include "iostream"
class RefTest {
public:
RefTest(double val = 4.0) : val_ref_(val) {}
private:
double &val_ref_;
};
int main() {
std::cout << sizeof(RefTest) << std::endl;
}
输出:
> 8
这里我们通过一个只包含引用的类来进行测试,最后输出的结果为8,我的测试环境是在64位系统,这不是正好一个指针的大小吗?说到这里想必你也已经明白了。
指针和引用底层实现
其实,通过引用和指针的汇编结果来说,其两者本质上都是一样的,都是一个地址。或许我们可以这样理解,从语言层面上讲引用可以理解为一个const指针;从底层的汇编机器码来说两者都是一个地址。
指针和引用的区别
- 指针是可以独立存在的; 但是引用不行
- 引用必须要进行初始化,指针没有必要
- 指针可以设置为NULL, 但是引用不行
- 引用一旦进行初始化之后,不会再改变其指向;但指针可以
左值引用和右值引用
大多数情况下,我们默认的引用就是左值引用,但是在C++ 11中添加了右值引用的概念。那么两者之间有什么区分吗?
- 可以取地址的,有名字的,非临时的就是左值;
- 不能取地址的,没有名字的,临时的就是右值;
可见立即数,函数返回的值等都是右值;而非匿名对象(包括变量),函数返回的引用,const对象等都是左值。
C++ 11中提供了std::move函数, 将一个左值引用转换成一个右值引用。但是需要注意两点:
- 如果你想要让一个对象能被move,就不要把这个对象声明为const。在const对象上的move请求会被默认地转换成拷贝操作。
- std::move事实上没有move任何东西,它甚至不能保证它转换出来的对象能有资格被move。你唯一能知道的事情就是,把std::move用在一个对象之后,它变成了一个右值。
REF
https://zhuanlan.zhihu.com/p/282846275
https://zhuanlan.zhihu.com/p/89175296
https://zhuanlan.zhihu.com/p/97128024