@引用详解
引用概念
引用:引用就是给变量取别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存。
例如:高中同学给我起的外号:疙瘩。疙瘩是我,我也是我
类型& 引用变量名(对象命)=引用实体
void test()
{
int a=10;
int& ra=a; // 定义引用类型
return 0;
}
引用特性
1.引用在定义时必须初始化
int& r; // 编译时会出错
2.一个变量可以有多个引用
int a=10;
int& ra = a;
int& rra = a;
3.引用变量与引用实体的类型必须要一致
int a=10;
int& ra = a;
double& rd = a; // 编译时会出错
4.引用一旦引用一个实体,再不能引用其他的实体,该引用在其生命周期内,只能和一个实体结合
引用使用场景
1.做参数
void Swap(int& left, int& right)
{
int tmp = left;
left = right;
right = tmp;
}
/// 建议:如果不想通过形参来改变外部实体的情况下,可以以const类型引用作为形参
void Print(const int& r)
{
cout << r << endl;
}
2.做返回值
// 错误示例:
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& result = Add(1, 2);
Add(3, 4); //result=7
Add(5, 6); //result=11
//Add函数运行结束后,它并没有清理栈帧中所留下的垃圾数据,
//因此:result是ret的别名,result可以看到空间中的垃圾数据。
return 0;
}
因此:以引用方式作为函数返回值时,一定不能返回函数栈上的空间(典型:局部变量),因为函数结束后,函数体内的局部变量就被销毁了。如果在外部以引用的方式来接收函数的返回值,外部的引用实际引用的就是一块非法的内存空间。
// 正确使用方法:
int& Count()
{
static int n = 0;
n++;
return n;
}
3.常引用
void Test()
{
const int a = 10;
//int& ra = a; //编译出错,a为常量
const int& ra = a;
//int& b = 10; //编译出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; //编译时出错,类型不同
const int& rd = d;
}
传值,传地址,传引用效率比较
在C++中,函数传参有三种方式:传值,传地址,传引用
传值:形参是实参的一份拷贝,传参的效率低,不会通过改变形参而改变外部的实参。
传地址:形参保存的是实参的地址,传参效率高,可以通过形参改变外部的实参。
传引用:形参实际是实参的别名,传递效率高,可以通过形参改变外部的实参-->可以达到与指针类似的效果,而且比指针可用性更高更安全。
引用和指针的区别
1.从概念上来看
指针是通过某个指针变量指向一个对象后,对它所指向的变量间接操作。
引用本身就是目标变量的别名,编译器不会给引用类型的变量开辟内存空间,引用与其引用的实体公用的是同一块内存空间,对引用的操作就是对目标变量的操作。
2.在底层实现上
引用实际是有空间的-->因为引用就是指针,它里面存储的是引用实体的地址。
引用和指针的不同点的总结:
1. 引用在定义时必须初始化,指针没有要求
2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型
实体
3. 没有NULL引用,但有NULL指针
4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占
4个字节)
5. 引用自加即引用变量所引用的实体增加1,指针自加即指针向后偏移一个类型的大小
6. 有多级指针,但是没有多级引用
7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全