关于c++中引用的基本用法

发布于:2023-01-04 ⋅ 阅读:(262) ⋅ 点赞:(0)


前言

引用是c++对c的重要扩充。在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递,那么引用和指针有什么不一样,又适合在哪些场景下使用呢?下面我将进行详细说明。


一、引用基本用法

1.引用基本语法

引用可以用来对一段连续的内存空间起别名,就像我们的父母给我们起小名那样。

基本语法:
Type& ref = val;
代码如下(示例):

void test01()
{
	int a = 10;
	int &b = a;//使用引用时,要在创建的变量前加&

	b = 20;

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

我们通过引用的方式给a起了别名b,a和b代表同一段内存空间,且a和b的地址都是相同的,因此,输出结果a,b都为20。

2.引用注意事项

2.1 必须初始化

2.2 一旦初始化后不能更改

代码如下

void test02()
{
	//int &a; 必须初始化
	int a = 10;
	int &b = a; //引用初始化后不可以修改了
	int c = 20;

	b = c; //赋值!!!
}

引用使用时必须进行初始化,且不能随意更改,就好比父母给我们起的小名不能随便给别人用一样。如果强行进行更改,将变成赋值操作。

3.对数组建立引用

void test03()
{
	int arr[10];
	for (int i = 0; i < 10;i++)
	{
		arr[i] = i;
	}

	//给数组起别名
	int(&pArr)[10] = arr;//这里注意int[10]是数组的类型
	for (int i = 0; i < 10;i++)
	{
		cout << pArr[i] << " ";
	}
	cout << endl;
}

二、函数的引用

1.参数的传递

值传递

void ValueSwap(int m,int n){
	int temp = m;
	m = n;
	n = temp;
}

地址传递

void PointerSwap(int* m,int* n){
	int temp = *m;
	*m = *n;
	*n = temp;
}

引用传递

void ReferenceSwap(int& m,int& n){
	int temp = m;
	m=n;
	n=temp;

通过引用传递和地址传递产生的效果是一样的,且引用的语法更简单。
(1)函数调用传参数时,参数前面不必加&
(2)被调用函数里不必再使用*
引用作为其他变量的别名存在,在一定程度上可以代替指针。c++鼓励用引用传递代替地址传递,因为语法更简单,更不容易出错。

2.注意事项

2.1 引用必须引一块合法内存

2.2 函数返回值不能返回局部变量的引用

代码如下

int& doWork()
{
	int a = 10;
	return a;
}
void test04()
{
	//int &a = 10; // 引用必须引一块合法的内存空间
	int &ret = doWork();
	cout << "ret = " << ret << endl; //第一次10是编译器做了优化
	cout << "ret = " << ret << endl;
	cout << "ret = " << ret << endl;
	cout << "ret = " << ret << endl;
	cout << "ret = " << ret << endl;
}

局部变量在返回之前会被销毁,所以将会引用一段非法内存空间。但我们对该返回值进行输出的话,发现可以依然输出成功,这是因为第一次输出是编译器对其进行了优化,如果后续继续使用,将会发生错误。

2.3函数引用返回值可以作为左值

int& doWork2()
{
	static int a = 10;
	return a;
}
void test05()
{
		int &ret = doWork2();
	//如果函数的返回值是引用,那么这个函数调用可以作为左值

	doWork2() = 1000; //相当于写了 a = 1000;
}

三、引用的本质

引用的本质在c++内部是一个指针常量
Type& ref = val; // 在编译器内部会进行如下转化,Type* const ref = &val;

c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。

//发现是引用,转换为 int* const ref = &a;
void testFunc(int& ref){
	ref = 100; // ref是引用,转换为*ref = 100
}

四、指针的引用

对指针进行引用,可以很好的避免二级指针的出现,下面为使用二级指针和使用指针引用的对比。

(1) 使用二级指针

struct Person
{
	int m_Age;
};

void allocatMemory(Person ** p) // **p 具体的Person对象  *p 对象的指针   p 指针的指针
{
	*p = (Person *)malloc(sizeof(Person));

	(*p)->m_Age = 100;
}

(2)使用指针引用

void allocatMemoryByRef(Person* &p)
{
	p = (Person*)malloc(sizeof(Person));
	p->m_Age = 1000;
}

可以看出,使用指针引用会使代码更加简洁,可读性更强

五、常量引用

1.const修饰的引用不能修改

const修饰的引用不能修改,但我们可以通过修改原名称的值进而修改const引用的值

void test01(){
	int a = 100;
	const int& aRef = a; //此时aRef就是a
	//aRef = 200; 不能通过aRef的值
	a = 100; //OK
	cout << "a:" << a << endl;
	cout << "aRef:" << aRef << endl;
}

2.const int &a = 10会分配内存

字面量本身是不具有内存的,将字面量赋值给普通引用会发生报错,但将字面量赋值给常引用,编译器会分配内存。

void test02(){
   //不能把一个字面量赋给引用
   //int& ref = 100;
   //但是可以把一个字面量赋给常引用
   const int& ref = 100;
    //加入const后,编译器的处理方式为 int temp = 200; const int& ret = temp;
}

3.引用时发生隐式类型转换

我们看下面这个代码:

double d=10.34// int& rd=d; 这么写编译器会报错,需要加const
const int& rd = d;

当我们用int类型的变量引用double类型的变量时,会发生隐式类型转换。这是rd实际上引用的不是d,而是d的一块临时拷贝。而临时拷贝都具有常性,是不能被修改的,所以需要在引用前面加上const。


总结

以上就是今天要讲的内容,本文仅仅简单介绍了一些关于引用的基本用法,欢迎大家进行评论和探讨。


网站公告

今日签到

点亮在社区的每一天
去签到