【是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 被覆盖为最新的值。

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


📝总结:

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

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

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



目录
相关文章
|
4月前
|
缓存 安全 编译器
C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者
《C++面试冲刺周刊》第三期聚焦指针与引用的区别,从青铜到王者级别面试回答解析,助你21天系统备战,直击高频考点,提升实战能力,轻松应对大厂C++面试。
479 132
C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者
|
4月前
|
存储 C++
C++语言中指针变量int和取值操作ptr详细说明。
总结起来,在 C++ 中正确理解和运用 int 类型地址及其相关取值、设定等操纵至关重要且基础性强:定义 int 类型 pointer 需加星号;初始化 pointer 需配合 & 取址;读写 pointer 执向之处需配合 * 解引用操纵进行。
461 12
|
6月前
|
算法 Java 数据库连接
Java 与 C++ 区别深入剖析及应用实例详解
本文深入剖析了Java和C++两种编程语言的区别,从编译与执行机制、面向对象特性、数据类型与变量、内存管理、异常处理等方面进行对比,并结合游戏开发、企业级应用开发、操作系统与嵌入式开发等实际场景分析其特点。Java以跨平台性强、自动内存管理著称,适合企业级应用;C++则因高性能和对硬件的直接访问能力,在游戏引擎和嵌入式系统中占据优势。开发者可根据项目需求选择合适语言,提升开发效率与软件质量。附面试资料链接:[点此获取](https://pan.quark.cn/s/4459235fee85)。
607 0
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
|
自然语言处理 前端开发 JavaScript
深入理解前端中的 “this” 指针:从基础概念到复杂应用
本文全面解析前端开发中“this”指针的运用,从基本概念入手,逐步探讨其在不同场景下的表现与应用技巧,帮助开发者深入理解并灵活掌握“this”的使用。
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
651 4
|
存储 数据可视化 C++
第九问:能否尽可能详细阐述指针和引用的区别?
在C++中,指针和引用是两个重要的概念,用于操作内存地址和数据。指针是一个存储内存地址的变量,可以动态分配和释放内存;引用是变量的别名,绑定后不可改变指向。指针提供更大的灵活性和控制力,适用于复杂内存操作;引用更直观,适合简化代码并提高可读性。根据实际需求选择合适的工具。
|
10月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
6月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
191 0
|
6月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
282 0