文章目录
前言
引用是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。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了一些关于引用的基本用法,欢迎大家进行评论和探讨。