对C++引用的理解

 在C++中,引用符号与取地址符号都是&,我们知道&用于取地址时,相当于取标识符的值的存储地址,那么如果该符号用于引用呢?

开门见山:C++中,如果标识符a是标识符b的引用,那么对a进行任何操作,都等效于对b进行操作。比如:

int b = 10;
int &a = b;  //a是b的引用


a = a + 1;  //等效于b=b+1,a与b的值都变成11

也可以这么比喻理解:有一个人的原名叫b,有一天他被别人起了个外号叫a,从此以后他有a、b两个称呼。不管a身上发生什么事情,都相当于b身上发生同样的事情,因为a和b是同一个人。

01

引用与指针

我们知道,指针的值是某一个存储地址,经常通过指针来操作其指向地址所存储的数据:

float a = 20.0;  //定义标识符a,并将a的值初始化为20.0
float *p = &a;   //定义指针p,并将指针p指向a的地址
*p = (*p) + 1;  //通过*p来改变a的值,等效于a = a + 1

而引用就是一个标识符的别名,对引用进行任何操作等效于对该标识符进行操作。我们也可以这么比喻理解:

比如某个城市叫“柳州市”,但是又因为该市的螺蛳粉在全国很出名,人们又称之为“螺蛳粉之乡”,“螺蛳粉之乡”是“柳州市”的别名,也相当于“柳州市”的“引用”,而“柳州市”的地址为“中华人民共和国广西壮族自治区柳州市”,沿着该地址可以到达该市,因此“中华人民共和国广西壮族自治区柳州市”相当于“柳州市”的“指针”。

那么,引用与指针的主要区别在哪里呢?请看下方:

1. 在创建引用的同时必须将其初始化,而指针可以在任何时候被初始化(const指针除外):

float a = 20.0;  
float &b = a;   //创建引用b的同时将b初始化,也即将b绑定到a,使其成为a的别名


float *p;  //创建指针p的时候,不是必须同时将其初始化
p = &a;  //可以在任何时刻将指针p初始化


//特殊情况,使用const修饰的指针也必须在定义的时候初始化
float *const p1 = &a;   //初始化指针p1指向a的地址之后,p1将不能改为指向其它地址
float x = 2.0;
p1 = &x;  //无效操作

2. 引用不能为NULL,其必须与某个标识符关联,然而指针却可以为NULL:

float &b = NULL;  //错误用法
float *p = NULL;  //把指针指向的地址设置为NULL,这是允许且很常见的做法

3. 引用被初始化之后就不能再更改引用关系,然而指针却可以在任何时候被更改为指向其它地址(const指针除外):

float a = 20.0;  
float &b = a;   //创建引用b的同时将b初始化,也即将b绑定到a,使其成为a的别名
float c = 5.0;
b = c;  //无效操作,引用b已经与a关联,不能再关联到其它标识符


float *p;  //创建指针p的时候,不是必须同时将其初始化
p = &a;  
p = &c;   //指针p可以任何时候更改为指向别的地址


//特殊情况,使用const修饰的指针也必须在定义的时候初始化,且初始化之后其指向的地址将不能更改
float *const p1 = &a;   //初始化指针p1指向a的地址之后,p1将不能改为指向其它地址
float x = 2.0;
p1 = &x;  //无效操作

03

引用的用途

C++中,引用最常见的用途为向函数传递输入参数,除了引用传递参数之外,常见的传递参数方式还有值传递、指针传递,下面我们分别举例来说明对比。

1. 值传递

使用值传递的方式,在函数内部不能改变输入参数的值,比如以下代码:

void func(int a)
{
    a = a * 5;
}


int main(void)
{
     int a = 6;
     func(a);
     printf("a=%d\n", a);  //这里打印出来a的值还是6,因为a以值传递的方式传入函数,函数内部并不能改变a的值
     return 0;
}

2. 指针传递

使用指针传递的方式,在函数内部可以改变输入参数的值,比如以下代码:

void func(int *a)
{
    (*a) = (*a) * 5;
}


int main(void)
{
     int a = 6;
     int *p = &a;  //定义指针p,并将其初始化为指向a的地址  
     func(p);   //将指针p传入函数
     printf("a=%d\n", a);  //这里打印出来a的值变成30,因为a以指针传递的方式传入函数,函数内部可以改变a的值
     return 0;
}

3. 引用传递

使用引用传递的方式,在函数内部也可以改变输入参数的值,比如以下代码:

void func(int &b)
{
    b = b * 5;
}


int main(void)
{
     int a = 6;
     func(a);   //将a的引用传入函数,也即引用b为函数的输入参数,将引用b与a关联,然后对b进行操作等效于对a操作
     printf("a=%d\n", a);  //这里打印出来a的值变成30,因为a以引用传递的方式传入函数,函数内部可以改变a的值
     return 0;
}

当需要在函数内部改变输入参数的值时,指针传递和引用传递都是可以做到的。那么为什么C++相对于C还增加了引用传递的方式呢:

1. 引用传递,在函数内部可以直接对传入的变量名进行操作,而不需要考虑是否在变量名前加“*”号。而且访问结构体/类的成员时是使用“.”就好,不需要考虑“->,减小出错的概率。比如以下代码:

typedef struct A
{
    int a;
    float b;
}A_obj;


//指针传递
void func(int *a)
{
     (*a) = (*a) + 5;  //需要在a前面加*号,这个*号也恰恰是容易忘记的地方
}


//引用传递
void func1(int &a)
{
     a = a + 5;  //直接对a操作即可,不需要在a前面加*号
}


//指针传递
void func2(A_obj *AA)
{
     AA->a = AA->a + 5;  //使用->访问成员a
     AA->b = AA->b * 5.0;  //使用->访问成员b
}


//引用传递
void func2(A_obj &AA)
{
     AA.a = AA.a + 5;    //使用.访问成员a
     AA.b = AA.b * 5.0;  //使用.访问成员b
}

2. 使用指针可以很自由地操作内存,其操作权限是非常大的,然而危险度、易错度也因权限的强大而增大。所以如果我们使用标识符的别名(引用)就可以完成我们想要的功能,那么就没必要犯更大的风险去使用指针了。

欢迎扫码关注本微信公众号,接下来会不定时更新更加精彩的内容,敬请期待~

posted @ 2021-06-01 20:39  萌萌哒程序猴  阅读(54)  评论(0编辑  收藏  举报