【是C++,不是C艹】 引用的概念 | 引用的使用 | 引用与指针的区别(一)

简介: 【是C++,不是C艹】 引用的概念 | 引用的使用 | 引用与指针的区别

前言


前面带大家学习了函数重载等C++基础,这期继续C++基础的学习:引用。


注:

最好是学完了C语言,并学过一些初阶的数据结构。


目录


Part1:何为引用

1.一个引子

2.概念

3.特征

4.常引用

Part2:使用场景

1.做参数

2.做返回值

Part3:有关引用的探讨

1.传值,传引用效率比较

2.引用和指针的区别


正文


Part1:何为引用


1.一个引子


不知道大家听没听过这个梗:

d6a019fa48f0d3a7cf95a69c3c9e7b43_619ea63c180d42dfb38ff13e428c3d38.jpeg

❓“抓捕周树人跟我鲁迅有什么关系”❓


这是《楼外楼》当中的一个桥段:


抓捕周树人跟我鲁迅有什么关系? 在我们看来,当然有关系啦,鲁迅是周树人的笔名之一啊。


其实周树人还有很多很多笔名,大家可以自行查阅... ...


不管先生有多少笔名,都是指的一个人:周树人。


☝️这就有引用的意思,接下来进入正题:


2.概念


你单看引用,想到的是语文课上的一种手法:此处运用了引用的手法,引用了... ... 的话,... ...


这种理解方式是有大偏差的,应该理解为 “起别名” :

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。


重点是 会为别名开辟内存空间


引用的使用:

类型& 引用变量名(对象名) = 引用实体;


   ☝️你以为是取地址? 错,这是放在数据类型后面的 & ,🐟取地址区分开!

void TestRef()
{
  int a = 10;
  int& ra = a; // 定义引用类型
  printf("%p\n", &a); // 输出a的地址
  printf("%p\n", &ra); // 输出ra的地址
}


👁️‍🗨️输出结果:

e7a4d22cc4f5bb5716a782298d8ede07_289aa8ba132b4250b3246647f1ec3e24.png

可见 a ra 是同一个变量,就像周树人和鲁迅指的是同一个人一样。

❓那我这样用行不行呢?

int a = 10;
char& ra = a; // 定义引用类型


❌可以看到报错:

d6ea42b6f536b61a35cdc046423d066b_f69e9f7225ec43f28d6b58a52a6ada01.png


也就是说:

引用类型必须和引用实体同种类型


3.特征


int& ra = a;
int& rra = a;


❓直接这样起别名会怎么样?

❌报错:

a22e8beffe0c07d99c5b37454f6f0a9d_53e8a1358e2147499f17adb6cab1377b.png


所以注意 引用在定义时必须初始化。

像周树人那样有多个笔名,我们也可以起多个别名

1. int& ra = a;
2. int& rra = a;

还有一个潜在的特征就是 一个引用只能对应一个实体,就像鲁迅就是指的是周树人。


📝特征总结:

• 引用在定义时必须初始化;

• 一个变量可以有多个引用;

• 引用一旦引用一个实体,再不能引用其他实体


4.常引用


什么是常引用呢? 就是面向常量的引用嘛

给常量起别名那些事:

1. const int a = 10;
2. int& ra = a;


❌报错:

也就是说给常量起别名时要用常引用:

1. const int a = 10;
2. // int& ra = a; // 该语句编译出错,a为常量
3. const int& ra = a; // 正确的


倒过来也一样:

int& b = 10; 
const int& b = 10;


❌报错:

be97321501c25d5c93b0e9167645b20d_87346a20420f49c48106576899534871.png


const int& b = 10; // 正确的
// int& b = 10; // 该语句编译出错,b为常量
const int& b = 10;

所以对待常量,引用也要使用常引用。


Part2:使用场景


学了引用,终归是要用于实践的,那么引用的使用场景有哪些呢?


1.做参数


没错,引用可以像指针那样做参数:

🌰例子:

void Swap(int& x, int& y)
{
  int temp = x;
  y = x;
  y = temp;
}


这是一个经典的交换函数,我们再看看指针版本的:

void Swap(int* x, int* y)
{
  int temp = *x;
  *y = *x;
  *y = temp;
}


但从形式上看,引用版的的却简洁,省去了解引用的步骤。

从效率上来看,也是引用版更胜一筹,因为引用变量不占用内存空间。

📝所以传参时大家可以考虑引用。


2.做返回值


先给你一段代码体会一下:

int& Add(int a, int b)
{
  int c = a + b;
  return c;
}
int main()
{
  int& ret = Add(1, 2);
  cout << ret << endl;
  return 0;
}


引用是不占有内存的,直接返回 c 的别名

655a332611894f354c362b4910cbc8f4_a20f9a9127b54ce2862bcdc2008f6ff1.png

说到这里,你应该反应过来了,这段代码是有问题的:


❌错误:


① 存在非法访问,当 Add 函数调用结束,栈帧销毁,c 的空间还给操作系统,而 c 的引用被 ret 接收,还是会去访问 c 的空间;


② 如果 Add 的栈帧销毁后,空间被清理,c 取到的就是随机值,紧接着 ret 接收的就是随机值。当然取决于编译器啦 ~


再来看看下面这段代码:

int& Add(int a, int b)
{
  int c = a + b;
  return c;
}
int main()
{
  int& ret = Add(1, 2);
  cout << ret << endl;
  Add(10, 20);
  cout << ret << endl;
  return 0;
}


👁️‍🗨️输出结果:

0dcee23f8185dbba19efd31c6f23818e_8dffd89f05584b9386630f2daf90ddb0.png

嘿?我寻思我也没动 ret 啊,怎么变了?

🪄 因为再次调用了 Add 函数,这次调用覆盖了之前已经销毁的栈帧,由于返回的是 c 的引用,而不是 c 本身,所以 ret 被覆盖为最新的值。

这个角度来看,使用引用作返回值条件还挺苛刻。


📝总结:

不要轻易使用引用作为返回值;

如果函数返回时,出了函数作用域,返回对象还没还给系统,则可以使用引用返回,

如果已经还给系统了,就老老实实使用传值返回。



目录
相关文章
|
5天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
18 4
|
21天前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
28天前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
1月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
35 1
|
1月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
28 2
|
1月前
|
C语言 C++
实现两个变量值的互换[C语言和C++的区别]
实现两个变量值的互换[C语言和C++的区别]
18 0
|
4天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
21 4
|
28天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
25 4
|
28天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
20 4
|
28天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
19 1