C++----引用

1. 引用的概念

引用C++语言中引入的概念,引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和引用的变量共用同一块内存空间就像一个人既有正名,也会有小名或者外号~

//引用的写法 引用变量类型& 引用变量名 = 引用实体

int main
{
    int a = 0;
    int& b = a;//b为a的引用
    return 0;
}
//引用的类型必须和引用的实体相同

 a,b共用一块内存。

2. 引用的特点

2.1 引用在定义时必须初始化

int main
{
    int a = 10;

    //int& b; 编译不通过
    int& b = a;
}

2.2 一个变量可以有多个引用

int main
{
    int a = 10;
    int& b = a;
    int& c = a;
    int& d = c;
}
// b、c、d都是a的引用

2.3 引用只能引用一个实体,引用后不能再引用其他实体

int main
{
    int a = 10;
    int& b = a;

    int c = 5;
    //b = c 编译不通过
}

3. 常引用

常引用就是在定义引用变量时,在前面加上一个const,和C语言中的const作用一样,将变量可读可写的权限变成只能可读的权限。相当于是权利缩小。

int main
{

    //引用的权利可以和实体相同,或者是缩小,但是不能放大。
    const int a = 10;
    //int& b = a;     实体权力为只读,引用权力放大,所以不正确;
    const int& b = a;
    
    //int& c = 10;    引用可以直接引用整数,但是整数具有常属性,所以权力应该是只读的。
    const int& c = 10;

    double d = 2.2;
    //int& e = d;    低精度类型引用实体时,会发生截断,在给e赋值时,中间会产生临时变量,d先赋值给                
    //               临时变量,再给到e,而临时变量具有常属性,所以引用要用const修饰。
    const int& e = d;
}

可以验证一下上面的第三个例子,e并不是d的引用: 

 在vs2019下,可以看到d和e的地址并不相同,就说明d和e并不使用同一块内存,所以d不是e的引用,间接证明,e赋值给d的时候是产生了临时变量,引用了临时变量。

常引用在函数传参时有时会起到一些作用,要注意引用的权限。 

4. 使用场景

4.1 引用做函数参数

交换数值在C语言中是用指针写的,用起来比较麻烦,在C++中可以用引用传参:

//用指针写的交换数值函数,用解引用,比较麻烦

void swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}


//用引用写的交换数值函数
//引用是实体的别名,所以直接交换两个引用,就是交换两个实体,方便容易理解

void swap(int& x, int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}

4.2 引用做函数返回值

4.2.1

 引用做函数返回值就是返回n的别名,不会产生新的临时变量。因为n由static修饰,所以n被储存在静态区,就算Count()函数执行完毕,n也不会被销毁。所以ret可以引用n的值。

如果不小心没加static,第一次打印ret也是可以打印出的,但是第二次打印就不能打印出1了。要解释这种现象就要从函数栈帧的角度看待这个问题。1.首先在第一次调用Count()函数时,n为1,这没问题,返回n的别名m(姑且称为m),但是问题是n是局部变量了,Count()函数调用完后n,m会随着它的栈帧一起销毁,为什么ret还能引用m呢?2. 因为函数栈帧只是通常意义上的“销毁”,里面的数据并没有马上销毁,它们仍然存在,只是操作系统不允许再访问了。而ret引用m,访问m是非法访问的!但是是可以访问的,(不允许但是可以,就像上中学时不允许学生谈恋爱,但是仍然有谈恋爱的。。)3. 输出函数也要调用函数栈帧,但是要先传参数,1先被传到了cout函数中,所以能被打印出来,但是这时Count()函数之前的栈帧已经被cout函数调用的栈帧覆盖了,原来n的位置也被覆盖了,所以在第二次调用cout时,ret不再是1了而是随机值!

 4.2.2

这个道理和上面的一样,第一次调用Add得到3,但由于栈帧内的数据仍然存在,所以ret能访问到c,ret引用c的引用d。第二次调用,函数栈帧仍然在原来的地方,不过c变成了11,所以引用d也是11,ret仍然是那一块内存,所以也是11。 

和4.21.相同,第一次可以打印出11,第二次由于刚刚调用了cout,所以属于c的那一块内存被覆盖了,c变成了随机值,因此打印出随机值。 

 为什么在这里可以打印出3,那是因为static修饰c只能被初始化一次!所以后面不管调用多少次答案都会是3。

 如果将c=a+b的代数式和定义c的语句分隔开,那么之后的每次每次都会执行a+b了。

由以上的例子可知: 如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已 经还给系统了,则必须使用传值返回。

4.3 传值传引用,返回值返回引用的效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。传值和引用在作为传参以及返回值类型上效率相差很大

5. 引用和指针的区别

  • 在语法层面来讲引用就是一个别名,没有独立空间,和其引用实体共用同一块空间;
  • 在底层实现方面来讲,引用是有空间的,并且实现方式和指针相同。

汇编语言: 

 虽然牌名不一样,但都是一个厂家生产的~

引用和指针的一些不同之处:

  1. 引用 在定义时 必须初始化,指针没有要求
2. 引用 在初始化时引用一个实体后,就 不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
3. 没有 NULL 引用 ,但有 NULL指针
4. 在 sizeof 中含义不同 引用 结果为 引用类型的大小 ,但 指针 始终是 地址空间所占字节个数 (32 位平台下占 4个字节 )
6. 有多级指针,但是没有多级引用
7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全  
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_w_z_j_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值