寒假学习使用C++(5)引用专题

本文详细介绍了C++中的引用概念,包括其基本定义、注意事项,以及在函数参数传递、值交换、结构体操作和作为返回值中的应用。通过比较指针,强调了引用作为常量指针的特性以及与指针的不同之处。
摘要由CSDN通过智能技术生成

一 引子

相信很多铁子们在学习C语言的时候有一个概念很是令各位感到头疼,那就是指针。可能学了一个学期指针是什么也还搞不清楚,那么学习C++就是你的福音,因为C++给你提供了一个新的类型,那就是引用

1:引用的基本概念

首先,我们可以简单粗暴的理解,引用就是一个数据的别名。

他的定义形式与指针类似,使用&(我们的老朋友,取地址符),上代码。

#include<iostream>
using namespace std;
int main()
{
	int a=10;
	int &y=a;
	cout<<y<<endl;
	return 0;
} 

​

这段代码的意思其实就是定义了一个引用指向了a变量,那么我们就可理解为此时y是a的一个别名,对y操作就是对a操作。

2:引用的注意事项

(1)与指针一样,在定义阶段,&是声明一个引用的意思而不是取地址的含义,&前面没有数据类型的时候是取地址的意思。
(2)引用一经声明必须初始化(就是必须指明他是谁的别名)
(3)引用一经声明不可改变,上代码。
#include<iostream>
using namespace std;
int main()
{
	int b=20,a=10;
	int &y=a;
	cout<<y<<endl;
	y=b;
	cout<<y<<endl;
	return 0;
} 
这里的y=b实质上是a=b的意思, 并不是改变y的引用为b 。故结果是10 20。
(4)可对引用再次引用。也就是说,一个变量的别名可以再有自己的别名,这样套娃下去(有点像指针里面的二级指针,三级指针)。
(5)引用是一种关系型声明,声明他和某一原有变量(实体)的关系,故而类型与原类型一致,并且不分配内存,与被引用的变量有相同的地址。

3:引用作为函数参数

1:交换数值

说了大半天引用的基本内容,我们来说一说为什么说引用可以让你对指针不再烦恼。

我们首先回忆我们在学习C语言的时候有一个经典的问题,就是如何实现两个数据数值的交换。

#include<iostream>
using namespace std;
void swap(int *a,int *b)
{
	int t;
	t=*a;
	*a=*b;
	*b=t;
}
void swap1(int &a,int &b)
{
	int t;
	t=a;
	a=b;
	b=t;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(&a,&b);
	cout<<"a = "<<a<<' '<<"b = "<<b<<endl; 
	cout<<"-----------"<<endl;
	a=10;
	b=20;
	swap1(a,b);
	cout<<"a = "<<a<<' '<<"b = "<<b<<endl; 

swap函数是我们熟知的函数的传址调用,而swap1是将a,b传递过去,用两个引用接收,操作起来看起来比指针简单多了。

看到这里不知你是否会有一个疑问,为什么不在主函数中声明两个引用,将引用当作参数传入函数,而是直接将a,b直接传递。

或者是说为什么swap1中函数参数是两个引用但是他们并没有初始化,为什么能够使用。

这是因为在调用这个函数的时候,函数接收到实参的传递后,形参就会初始化。

(2)结构体传参

#include<iostream>
using namespace std;
struct stu
{
	int age;
	char name[20];
};
void prints(stu &s)//stu &s == s1 引用的函数传参 
{
	cout<<s.age<<" "<<s.name<<endl;
}
int main()
{
	stu s1={10,"zhangsan"};
	prints(s1);
	return 0;
}

这里注意对结构体的操作是.操作符而不是->操作符。因为s是s1的一个别名。

在这里也没有发生值拷贝的动作。

4:引用的实质

在上文中我们不断提到指针与引用有很多相似的地方,那么他们之间是否有某些特殊的关系呢

为了容易理解和印象深刻我们先看一段代码

#include<iostream>
using namespace std;
struct a
{
	int *a;
};
struct b
{
	int &a;
};
int main()
{

	cout<<sizeof(struct a)<<endl;	
	cout<<sizeof(struct b)<<endl;
} 

我们会发现,引用与指针所占用的大小是一样的。又结合引用必须初始化这与经const修饰的值也一样。所以我们可以大胆的推测,引用是一个常指针。

当我们将引用作为函数参数传递的时候,编译器会替我们将实参取地址给引用接收。

同样对一个引用操作赋值的时候,编译器会替我们隐藏进行取*操作。

我们在研究引用的时候可以当作一个常量指针这样去研究,但是编程的时候就当作一个值的别名就行。

常量指针

简单的举一个例子,我们定义一个数组,我们知道数组名其实就是一个指针。但是有没有想过他在哪里储存。比如int arr[10];真正在栈上开辟的是是个整形,而数组名其实在常量区的一个指针,指向这部分区域。

5:引用作为函数的返回值

既然引用的本质是一个指针,那么他跟指针在作为函数返回值的时候就有类似的特征。

引用作为返回值不要返回局部变量的引用,会出现栈上数据被释放的情况,而因返回一个不随函数结束生命结束的变量(堆上的变量 静态变量 全局变量)。

#include<iostream>
using namespace std;
int& geta()
{
	int a=10;
	return a;
}
int main()
{
    int &main_a_re=geta();
    cout<<main_a_re<<endl;
	cout<<main_a_re<<endl;      
	return 0;
} 

就像这个代码,编译器会报警告,提醒我们不应该返回一个局部变量a,因为geta中的a随着函数的结束而被销毁(因为是在栈区上开辟的,函数结束时会被操作系统自动回收)。故而第一次可以打印出来,正常出栈。而第二次打印,我们已经失去了对这块区域的访问权限,故而拿不到正确的a值。所以我们在用引用作为返回值的时候应该使用上文提到的变量形式。

正确操作如下,当然使用malloc等动态开辟的内存也是可以的,不过要记得释放。

#include<iostream>
using namespace std;
int& geta()
{
	static int a=10;
	return a;
}
int main()
{
    int &main_a_re=geta()
    cout<<main_a_re<<endl;
	cout<<main_a_re<<endl;      
	return 0;
} 

最后一点,既然返回值是一个引用(一个数据的别名)而不是一个数值,那么返回值是引用的函数是可以作为左值。而且引用返回一个函数值的最大好处是,在内存中不产生被返回值的值拷贝。

6:指针引用

现在有个问题,如何通过指针来改变此指针指向区域的值,要求用函数实现。

#include<iostream>
using namespace std;
//二级指针版
void  change_a_1(int** p)
{
	int a = 1000;
	*p = &a;
}
//指针引用版
void  change_a_2(int* &p)//拆开理解 int*类型的引用p 
{
	int a = 1000;
	p=&a;
}
int main()
{
	int* p = NULL;
	change_a_1(&p);
	cout << *p << endl;
	change_a_2(p);
	cout << *p << endl;
    return 0; 
} 

首先我们定义一个指针变量,第一种方法是取指针p的地址当作参数,用二级指针接收。可这样的话对于指针掌握不熟练的铁子们就不太友好。所以我们选择对指针进行引用。

7:const+引用

1:如果想对一个常量进行引用, 必须是一个const引用。
2:相反如果一个普通变量,用一个const引用接收是可以的。

3:const 修饰引用,一般跟const修饰指针的用途是一样的。都是作为函数参数保证该参数是输入参数,是只读的,在函数内部该表不了外部的值。

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值