引用是C++编程语言中非常重要的一部分内容,它与C语言中的指针作用类似,但本质却不同,本文将详细介绍C++的引用,如有不足或者缺漏之处,欢迎指正
与指针的区别
我们说引用和指针作用类似,因为他都可以通过另一个变量名B去引用(指向)变量A,然后通过对各自的操作,访问到A的内容.
int a = 3;
int* p = &a;
int& b = a;
cout <<"指针:"<< * p << "\n引用:" << b;
//两个输出全为3
指针是一个占内存的变量,占内存大小受编译环境的影响(在32位操作系统上占4字节,在64位操作系统上占8字节),只不过这个内存当中存放的是变量a的地址,所以我们经过取*访问,可以得到a的内容3
引用不占用内存(底层实现需要内存),它是直接与这个变量用同一块内存空间,所以我们说引用也是给变量起别名,它的本质是指针常量,即 *const p 所以必须要初始化,且不能改变引用关系
int a = 3;
int* p = &a;
int& b = a;
cout << &p << " " << &a << endl;
//输出两个 不同的 地址
cout << &b << " " << &a;
//输出两个 相同的 地址
引用的分类
引用分为左值引用,右值引用和万能引用,在搞清楚这些引用之前,我们先了解一下什么是左值和右值
左值: 通俗来说就是可以放在等号左面的值,可以找到对应的内存地址,进行修改,例如已经定义了的变量
右值: 只能放在等号右边,一般是常量,或者返回值,匿名对象,匿名函数Lambda,这些值的内存地址我们无法找到,也无法更改其中的内容
左值引用,顾名思义只可以引用左值,右值引用同理,万能引用既可以引用左值也可以引用右值,它们在语法的定义上也有不同
左值引用语法: 数据类型&变量 = 左值;
右值引用语法: 数据类型&& 变量 = 右值;
万能引用语法: const 数据类型& 变量 = 左右值; 万能引用有const修饰,所以也能防止引用修改原数据,起到保护数据的一种效果
int a;
int& b = a;
//int& c = 2;//编译错误,无法将右值绑定到左值引用
int&& d = 2;
//int&& e = a;//编译错误,无法将左值绑定到右值引用
const int& f = a;
const int& g = 2;
引用做函数参数
函数的传参形式分为两种,一种是值传递(指针传参在本质上也是值传递,将实参指针的值传给形参指针,只不过内容是变量的内存地址),一种是引用传递
我们可以使用引用来简化指针修改实参
void m_swap(int& a, int& b) {
int t = a;
a = b;
b = t;
}
int main() {
int a = 3, b = 4;
cout << a << " " << b << endl;
//输出3 4
m_swap(a, b);
cout << a << " " << b << endl;
//输出4 3
return 0;
}
引用接收函数返回值
引用可以接收函数的返回值,但是,不要让引用接收返回的局部变量,因为在调用的函数内部,执行return 语句会生成一个临时的匿名对象用来给接收返回值的参数赋值,匿名对象仅存在于当前行,但是局部变量在函数体执行完毕之后会有系统回收对应的资源,导致这一块内存空间被重置,里面的数据成为随机值,这样引用接收的变量也会失去里面的内容
int& test01() {//返回值为int的引用,返回的是变量a而不是a的值
int a = 10;
return a;
}
int& test02() {
static int a = 20;//静态成员的生命周期一直到程序结束
return a;
}
int main() {
int& ans01 = test01();
cout << "ans01= " << ans01 << endl;
//输出10
cout << "ans01= " << ans01 << endl;
//输出随机值
int& ans02 = test02();
cout << "ans02= " << ans02 << endl;
//输出20
cout << "ans02= " << ans02 << endl;
//输出20
return 0;
}
需要注意的是,有些电脑在第二次输出ans01时还是10,这是因为不同的电脑清理内存的机制可能不同,但决不代表这种行为是安全的,应当注意
注意事项
- 引用必须初始化,且不能空
- 引用不能改变引用关系
- 引用作为返回值可以避免拷贝构造,但是要注意避免返回局部变量引用
- 引用作为函数参数传参时,也可以避免拷贝构造,但是在函数内部对该对象进行的操作会影响到原本的对象,因为他们在内存上公用同一块内存