【三】C++引用机制

本文介绍了C++中的引用机制,包括引用的概念、意义、const引用、引用的本质以及在函数返回和三目运算符中的应用。引用作为变量的别名,增强了代码的可读性和实用性,其本质是一个指针常量,但不能直接获取引用自身的存储空间地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 
 

1、变量名回顾

  • 变量是一段实际连续存储空间的别名
  • 程序中通过变量来申请并命名存储空间
  • 通过变量的名字可以使用存储空间

 
如图:
这里写图片描述

 
思考:
  对于一段连续的存储空间只能有一个别名吗?
肯定不是,因此我们可以给同一个连续的存储空间取多个别名,这就诞生了C++中的引用机制

 
 

2、引用的概念

  • 引用可以看作一个已定义变量的别名
  • 引用的语法:Type& name = var;

 
示例:

exp-1.cpp

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a = 4;
    int& b = a;

    b = 5;

    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("&a = %p\n", &a);
    printf("&b = %p\n", &b);

    return 0;
}

 
运行结果:

这里写图片描述

可见,a和b指向了同一内存块!

Tip:
  普通引用在声明时必须用其它的变量进行初始化

 
 

3、引用的意义

  • 引用作为其它变量的别名而存在,因此在一些场合可以代替指针
  • 引用相对于指针来说具有更好的可读性实用性

 
示例:

exp-2.cpp

#include <stdio.h>

void swap_q(int& a, int& b)
{
    int temp = a;
    a = b;
    b = temp;
}

void swap_p(int *pa, int *pb)
{
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}


int main(int argc, char *argv[])
{
    int a = 4;
    int b = 5;

    swap_q(a, b);
    printf("a = %d\n", a);
    printf("b = %d\n", b);

    printf("******************************\n");

    swap_p(&a, &b);
    printf("a = %d\n", a);
    printf("b = %d\n", b);

    return 0;
}

 
Tip:
  引用作为函数参数声明时可不进行初始化

 
 

4、const引用

  • 在C++中可以声明const引用
    • const Type& name = var;
  • const引用让变量拥有只读属性

示例:

exp-3.cpp

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a = 4;
    const int& b = a;
    int* p = (int*)&b;

    //b = 5; //这里编译器会报错,不能修改只读变量的值

    *p = 5; //这里成功修改了变量a的值

    printf("a = %d\n", a);
    printf("b = %d\n", b);

    return 0;
}

运行结果:

这里写图片描述

 
Tip:
  从运行结果来看,指向变量a的指针还是能成功的改变a的值,所以,此时只是无法通过常量引用b来修改a的值!

 

  • 当使用常量const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名

 
示例:

exp-4.cpp

#include <stdio.h>

int main(int argc, char *argv[])
{
    const int& b = 1;       //编译器会为其分配存储空间
    int* p = (int*)&b;

    //b = 5;    //这里会报错,因为常量引用为只读

    *p = 5;     //还是可以通过指针来更改其值

    printf("b = %d\n", b);

    return 0;
}

 
Tip:
  使用常量const引用初始化后将生成一个只读变量

 
经典问题:

#include <stdio.h>

int main()
{
    int y = 10;
    const int z = y; //试图用一个变量给一个常量赋值,来定义一个常量,但是这样做,z真的是一个常量么?
//验证方法:
    //取z的地址,让编译器为它分配空间,然后把它的地址强制去掉底层const属性,通过指针向其地址空间赋值,最后看z的输出值是否改变,如果改变说名不是常量,是只读变量!

    int *p = const_cast<int*>(&z);
    *p = 7;

    printf("z = %d\n", z);
    printf("*p = %d\n", *p);

    return 0;
}

运行结果:

这里写图片描述

从运行结果来看,上面的z是一个只读变量,而非常量!

#include <stdio.h>

int main()
{
    char c = 'c';
    char& rc = c;
    const int& trc = c; //用一个变量给一个常引用赋值
    //已经知道,如果 const int& i = 1; 编译器会为它分配存储空间,从而生成一个只读变量
    //而这里也是一样的,trc最终会是一个只读变量

    rc = 'a';

    printf("c = %c\n", c);
    printf("rc = %c\n", rc);
    printf("trc = %c\n", trc);

    return 0;
}

运行结果:

这里写图片描述

 
 

5、引用的本质

思考:

  引用有自己的存储空间吗?

测试:

exp-5.cpp

#include <stdio.h>

struct TRef
{
    int& a;
    int& b;
};

int main(int argc, char *argv[])
{

    printf("sizeof(TRef) = %ld\n", sizeof(TRef));

    return 0;
}

 
运行结果:

这里写图片描述

 
Tip:
  从上面的结果可知,引用是有存储空间的,那么这又是为什么呢?

 
- 引用在C++中的内部实现是一个指针常量
- Type& name <==> Type* const name

 
Improtance:

  • C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
  • 从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间,这是C++为了实用性而做出的细节隐藏

 
思考:
  既然引用变量有自己的存储空间,如何获得它的该空间的地址?

解答:
  无法通过“&”取地址符,获得“引用变量”自身的存储空间的地址,通过“&”取地址符得到的是它的目标变量的地址,引用机制在一开始设计时,就没打算让我们操作它本身,对它的所有操作都反应在目标变量上;而且,引用机制很容易被编译器优化,优化后,原来访问引用变量的代码编译成的机器代码,实际上是直接访问原变量,这样的话,编译器甚至可以不为引用变量分配空间,全部直接访问它所引用的原变量。

 
举例:

exp-6.cpp

#include <stdio.h>

struct TRef
{
    int& a;
    int& b;
    int& c;
};

int main(int argc, char *argv[])
{
    int a = 1;
    int b = 2;
    int c = 3;
    TRef rA = {a, b, c};


    printf("&a = %p\n", &a);
    printf("&rA.a = %p\n", &rA.a);

    printf("&b = %p\n", &b);
    printf("&rA.b = %p\n", &rA.b);

    printf("&c = %p\n", &c);
    printf("&rA.c = %p\n", &rA.c);

    printf("&rA = %p\n", &rA);
    printf("sizeof(TRef) = %ld\n", sizeof(rA));

    return 0;
}

 
运行结果:

这里写图片描述

 
Tip:
  可见,对引用变量取地址,实际上得到的是其目标变量的地址!

 
 

6、函数返回引用

  • 若返回栈变量
    • 不能成为其它引用的初始值
    • 不能作为左值使用
  • 若返回静态变量全局变量
    • 可以成为其他引用的初始值
    • 即可作为右值使用,也可作为左值使用

示例:

exp-7.cpp

#include <stdio.h>

int& f()
{
    static int a = 0;   //静态局部变量,函数退出后不释放

    return a;
}

int& g()
{
    int a = 0;      //局部变量,函数退出后释放

    return a;
}

int main()
{
    int a = g();
    int& b = g();

    f() = 10;   //C中编译不过,
                //C++中f函数返回的引用可作为左值使用,这一点类似于三目运算符

    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("f() = %d\n", f());

    return 0;
}

 
运行结果:

这里写图片描述

 
Tip:
  首先,g函数执行后,将局部变量的引用返回并赋值给了变量a,所以a的值为0;然后,g函数执行后,将局部变量赋值给一个引用变量b,然后由于g函数执行完毕,系统释放了调用g函数时的栈空间,导致b此时成为了一个未知内存块的引用,所以访问它得到的是随机值;最后,f函数执行后将它的静态局部变量的引用返回,然后作为左值给它赋值,所以值为10。

 
 

7、C++对三目运算符做了什么?

  • 当三目运算符的可能返回都是变量时,返回的是变量的引用
  • 当三目运算符的可能返回中有常量时,返回的是常量值

 
 

8、小结

  • C++中的引用可以看作变量的别名来使用
  • C++中的常引用可以使得一个变量拥有只读属性
  • C++中的常引用可以用常量初始化而得到一个只读变量
  • C++中引用的本质是一个指针常量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值