C++引用深度详解
C++引用深度详解
- 前言
- 1. 引用的本质与核心特性
- 1.1 引用概念
- 1.2 核心特性
- 2. 常引用与权限控制
- 2.1 权限传递规则
- 2.2 常量引用
- 2.3 临时变量保护
- 1. 样例
- 2. 样例
- 3. 测试
- 三、引用使用场景分析
- 3.1 函数参数传递
- 输出型参数
- 避免多级指针
- 高效传参
- 3.2 做函数返回值
- 正确使用
- 危险案例
- 4. 性能对比实验
- 4.1 参数传递效率
- 4.2 返回效率对比
- 5. 引用与指针的终极对比
- 5.1 底层实现
- 5.2 特性对比表
- 6. 高级应用技巧
- 6.1 链式操作
- 7. 总结引用要点
- 8. 最佳实践指南
前言
本文深度探索引用的各种用法和特性。介绍引用的语法,核心特性,引用的权限控制,常引用以及引用的各种使用场景。
1. 引用的本质与核心特性
1.1 引用概念
引用(Reference)是C++引入的重要特性,是 C++ 中的一种数据类型。
从语法层面讲,引用是变量的别名。与指针不同,引用在语法层面不开辟新空间,而是与原变量共享内存地址。
引用不会创建新的对象,只是创建另一个访问现有对象的方式,引用类型变量是已有变量的别名。
引用在语法上与指针类似,但其语义和使用方式不同。
我们创建一个变量,其实就是对一块内存空间
取名字
而创建引用类型对象,就是对已有的一块空间取第二个名字。
两个名字代表的是同一块空间。
int main() {int a = 10;int& ra = a; // ra是a的别名ra = 20; // 修改ra等同于修改acout << a; // 输出20
}
可以看到:
- 对ra进行操作,也就是对a进行操作。
- 变量
ra
和ra
具有相同的地址。
1.2 核心特性
特性 | 说明 | 示例验证 |
---|---|---|
必须初始化 | 定义时必须绑定实体 | int& r; 编译错误 |
不可重绑定 | 绑定后不能指向其他变量 | int b=20; ra=b; 实为赋值 |
类型严格匹配 | 必须与实体类型一致 | double d=1.1; int& rd=d; 错误 |
多级别名支持 | 可对引用再次引用 | int& rra=ra; 合法 |
int main() {int a = 666;int num = 100;int& b = a;int& c = b; //可对引用再次引用int& d = c; //可对引用再次引用//int& e; //引用必须初始化,该语句编译会报错。cout << d << endl;d = num; //引用一旦指定,不可修改 所以这里是 赋值, 是把num的值 100 赋值给 d cout << &a << endl; //输出的地址相同cout << &b << endl;cout << &c << endl;cout << &d << endl;cout << a << endl; //输出的值相同cout << b << endl;cout << c << endl;cout << d << endl;
}
- 引用必须初始化
- 引用一旦指定,不可重绑定
- 引用的类型严格匹配
- 一个变量可以有多个引用(多个别名),引用变量也可以有引用(引用的别名)。
- 在语法层面上, 我们认为 引用没有开辟新空间, 只是对同一片内存空间取了多个名字
2. 常引用与权限控制
2.1 权限传递规则
操作 | 合法性 | 说明 |
---|---|---|
变量 → 常引用 | ✔️ | 权限缩小 |
常量 → 非 常引用 | ❌ | 权限放大 |
常引用 → 常引用 | ✔️ | 权限不变 |
注意事项:
- 权限可以平移。
- 权限可以缩小。
- 权限不能放大。
看如下,此处有报错,为什么?
1. 首先声明,每个变量名都有其相应的权限。
2. 也就是说,每块内存,都有相应的权限。
3. 引用,就是对一块内存起了别名
int x = 0
, 创建变量xint& y = x
, y是x的引用。此处, y是int型的引用,发生了权限的平移。const int& z = x
, 此处发生了权限的缩小。该内存块在使用名字z
时,权限为const,不能修改。- 名字x和y权限相同,即, 该内存块在使用名字
x
和y
时,可以修改 - 因此
++x
正确,++z
会报错。
2.2 常量引用
int main() {const int a = 10;//int& ra = a; //编译出错,因为 a为常量const int& ra = a; //正确写法//int& b = 10; //编译出错,因为 10 为常量, 该语句产生了权限的放大const int& b = 10; //正确写法return 0;
}
- 对
const int a = 10;
, 有int& ra = a
, 编译出错,因为 a为常量, 该语句发生了权限的放大
2.3 临时变量保护
1. 样例
声明1:在C/C++中,只要发生类型转换,就会产生临时变量。
声明2:临时变量具有常性(不能修改)。
类型转换时会产生具有常性的临时变量,看以下例子:
double d = 12.34;//int& rd = d; //编译出错,因为 类型不同const int& rd = d; // 合法,等价于:// int temp = d; // d为3.14, 常量// const int& rd = temp;
以上过程如下:
- 引用时发生类型转换,实质上是对临时变量的引用。
- 临时变量具有常性。
double d = 12.34
,//int& rd = d; //编译出错
。临时变量具有常性
,实质上可以理解为:int& rd = const temp
发生了权限的放大,因此报错。 - 临时变量具有常性。
const int& rd = d
, 实质上可以理解为:const int& rd = const d
, 是权限的转移。因此正确。
2. 样例
声明3:函数在进行值返回时,返回的同样是临时变量。该临时变量是原函数的拷贝。
实际上返回的是具有常性的临时变量
。
清楚了这一点后,以下例子的原理同上。
//例子
int func1() { //返回x的拷贝,会产生临时变量static int x = 10;return x;
}int& func2() { //返回x的别名, 不会产生临时变量static int x = 10;return x;
}
int main() {//int& x = func1(); //权限放大,错误。int x1 = func1(); // 仅拷贝const int& y = func1(); //权限平移,可以进行int& ret2 = func2(); //可以,权限的平移 const int& ret2_ = func2(); //可以,权限的缩小//总结,func返回的是一个变量的别名, return 0;
}
3. 测试
//测试类型转换时会产生临时变量
int main() {int i = 10;double j = 10.11;//过程:double temp = i; double j = temp//该过程会发生类型提升//一般是小的往大的进行类型提升,提升的时候不能改变原变量。//因此只能产生原变量的副本,即临时变量if (j > i) //此处是 double j 和 double i的比较cout << "xxxxxxxxxxxxx" << endl;return 0;
}
运行结果如下:
三、引用使用场景分析
3.1 函数参数传递
输出型参数
//利用引用,可以避免指针和多级指针
void Swap(int& a, int& b) { //交换值 形参是实参的别名int temp = a;a = b;b = temp;
}
避免多级指针
void Swap(int*& a, int*& b) { //交换指针 如果不用引用,交换指针变量需要用二级指针int* temp = a;a = b;b = temp;
}
高效传参
struct BigData { int arr[10000]; };// 值传递:拷贝4w字节
void ProcessData(BigData data); // 引用传递:仅传地址(4 或 8字节)
void ProcessDataOpt(const BigData& data);
3.2 做函数返回值
正确使用
int& GetStatic() {static int count = 0;return count; // 静态变量, 生命周期足够
}
危险案例
int& DangerousRet() {int local = 10;return local; // 返回局部变量引用!
}
- 如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回。
- 如果已经还给系统了,则必须使用传值返回。
不能返回局部对象(变量)的引用。
4. 性能对比实验
4.1 参数传递效率
struct HugeStruct { int data[10000]; };void ValueFunc(HugeStruct hs) {} // 值传递
void RefFunc(const HugeStruct& hs) {} // 引用传递// 测试结果(10000次调用):
// 值传递耗时:1587ms
// 引用传递耗时:2ms
4.2 返回效率对比
HugeStruct g_data;HugeStruct ReturnByValue() { return g_data; }
HugeStruct& ReturnByRef() { return g_data; }// 测试结果(100000次调用):
// 值返回耗时:3521ms
// 引用返回耗时:1ms
5. 引用与指针的终极对比
5.1 底层实现
; 引用实现
mov dword ptr [a], 0Ah
lea eax, [a] ; 取地址
mov dword ptr [ra], eax ; 指针实现
mov dword ptr [a], 0Ah
lea eax, [a]
mov dword ptr [pa], eax
关键区别
- 引用:在 C++ 中引用通常会被优化为指针,底层是通过地址访问,但语法上没有指针的显式解引用和取地址操作。
- 指针:指针显式地存储内存地址,允许进行指针算术操作,指针本身也可以为空(
nullptr
)。
从底层来看,引用和指针的实现非常相似,都是通过存储地址来实现对变量的间接访问。区别在于语法和语义上,引用在 C++ 中看起来更像是变量的别名,而指针则显式地表示地址。
5.2 特性对比表
特性 | 引用 | 指针 |
---|---|---|
初始化要求 | 必须 | 可选 |
空值 | 无NULL引用 | 支持NULL |
重定向 | 不可 | 可以 |
访问方式 | 直接访问 | 需解引用(*或->) |
类型安全 | 更高 | 较低 |
多级间接 | 单级 | 支持多级 |
sizeof | 返回原类型大小 | 返回地址大小(4或8字节) |
6. 高级应用技巧
6.1 链式操作
struct Matrix {Matrix& Transpose() { /*...*/ return *this; }Matrix& Rotate(double angle) { /*...*/ return *this; }
};Matrix mat;
mat.Transpose().Rotate(45); // 链式调用
7. 总结引用要点
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
8. 最佳实践指南
- 优先const引用:函数参数尽量使用
const T&
形式 - 警惕返回引用:确保返回对象生命周期足够
- 替代输出参数:用引用替代指针作为输出参数
- 类型转换注意:隐式转换产生临时变量需用const引用
- 与智能指针配合:
std::shared_ptr<T>&
管理资源(后续讲解)
以上就是关于引用的所有内容了,码字整理不易,欢迎各位大佬在评论区交流
相关文章:

C++引用深度详解
C引用深度详解 前言1. 引用的本质与核心特性1.1 引用概念1.2 核心特性 2. 常引用与权限控制2.1 权限传递规则2.2 常量引用2.3 临时变量保护1. 样例2. 样例3. 测试 三、引用使用场景分析3.1 函数参数传递输出型参数避免多级指针高效传参 3.2 做函数返回值正确使用危险案例 4. 性…...

C++ Primer 语句作用域
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
github - 使用
注册账户以及创建仓库 要想使用github第一步当然是注册github账号了, github官网地址:https://github.com/。 之后就可以创建仓库了(免费用户只能建公共仓库),Create a New Repository,填好名称后Create,之后会出现一些仓库的配置信息,这也是一个git的简单教程。 Git…...

内网ip网段记录
1.介绍 常见的内网IP段有: A类: 10.0.0.0/8 大型企业内部网络(如 AWS、阿里云) 10.0.0.0 - 10.255.255.255 B类:172.16.0.0/12 中型企业、学校 172.16.0.0 - 172.31.255.255 C类:192.168.0.0/16 家庭…...

k8s部署logstash
1. 编写logstash.yaml配置文件 --- apiVersion: v1 kind: Service metadata:name: logstash spec:type: ClusterIPclusterIP: Noneports:- name: logstash-tcpport: 5000targetPort: 5000- name: logstash-beatsport: 5044targetPort: 5044- name: logstash-apiport: 9600targ…...

EF Core中实现值对象
目录 值对象优点 值对象的需求 值类型的实现 值类型GEO的实现 值类型MultilingualString的实现 案例:构建表达式树,简化值对象的比较 值对象优点 把有紧密关系的属性打包为一个类型把领域知识放到类的定义中 class shangjia {long id;string nam…...

【分布式理论9】分布式协同:分布式系统进程互斥与互斥算法
文章目录 一、互斥问题及分布式系统的特性二、分布式互斥算法1. 集中互斥算法调用流程优缺点 2. 基于许可的互斥算法(Lamport 算法)调用流程优缺点 3. 令牌环互斥算法调用流程优缺点 三、三种算法对比 在分布式系统中,多个应用服务可能会同时…...

木材表面缺陷检测数据集,支持YOLO+COCO JSON+PASICAL VOC XML+DARKNET格式标注信息,平均正确识别率95.0%
数据集说明 木材表面缺陷检测数据集是用于训练和验证人工智能算法,以帮助自动识别和检测木材表面的缺陷,如裂纹、疤痕、孔洞等。这对于木材行业非常重要,可以提高生产过程的效率和质量控制水平。 本文提供的木材表面缺陷检测数据集࿰…...
Leetcodehot 力扣热题100 二叉搜索树中第 K 小的元素
class Solution { public:int res; // 用于存储第 k 小的元素int kthSmallest(TreeNode* root, int k) {inorder(root, k); // 进行中序遍历并找到第 k 小的元素return res; // 返回结果}private:// 中序遍历:遍历树的左子树、根节点和右子树void inorder(TreeNod…...
Awtk 如何添加开机画面
场景 我们知道在工程中,Ui是一个线程,并且需要一直存在,当我们使用的开机画面在这个线程开启就直接展示的时候,因为awtk的界面是window_open入栈的,即首次打开的窗口会记录在top,往后的窗口会依次往后存放&…...

关于多语言商城系统的开发流程
建设多语言商城系统是现在很多传统外贸企业的选择,外贸企业通过多语言电商系统开展海外业务,那么多语言商城系统的开发流程是怎么样的呢?接下来就跟着小来一起来看看吧。 1、页面UI设计 多语言商城系统的原型图经过反复推敲修正后࿰…...

IDEA中常见问题汇总
🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢…...

计算机视觉-拟合
一、拟合 拟合的作用主要是给物体有一个更好的描述 根据任务选择对应的方法(最小二乘,全最小二乘,鲁棒最小二乘,RANSAC) 边缘提取只能告诉边,但是给不出来数学描述(应该告诉这个点线是谁的&a…...

CSS 实现下拉菜单效果实例解析
1. 引言 在 Web 开发过程中,下拉菜单是一种常见且十分实用的交互组件。很多前端教程都提供过简单的下拉菜单示例,本文将以一个简洁的实例为出发点,从 HTML 结构、CSS 样式以及整体交互逻辑三个层面进行详细解析,帮助大家理解纯 C…...
DeepSeek模拟阿里面试——Mysql
1.数据库基础知识 关系型数据库是什么? 关系型数据库是基于关系模型的数据库,使用表格来存储数据,表格之间可以通过键建立关系。 数据库的ACID特性是什么? 原子性(Atomicity):事务要么全部完成…...
MVVM设计模式
MVVM(Model-View-ViewModel)是一种软件设计模式,MVVM模式由三个主要部分组成: Model(模型):负责管理应用程序的业务逻辑和数据。它不关心UI如何展示数据,主要负责与服务器通信和数据处处…...
解决:Cannot find a valid baseurl for repo: base/7/x86_64
传送门 repo_file/etc/yum.repos.d/CentOS-Base.repo cp ${repo_file} ~/CentOS-Base.repo.backup sudo sed -i s/#baseurl/baseurl/ ${repo_file} sudo sed -i s/mirrorlist.centos.org/vault.centos.org/ ${repo_file} sudo sed -i s/mirror.centos.org/vault.centos.org/ $…...
ffmpeg -codecs
1. ffmpeg -codecs -loglevel quiet 显示ffmpeg支持的编解码器 2. 输出 选取部分结果: Codecs: D..... Decoding supported .E.... Encoding supported ..V... Video codec ..A... Audio codec ..S... Subtitle codec ...I.. Intra frame-only code…...

社区版IDEA中配置TomCat(详细版)
文章目录 1、下载Smart TomCat2、配置TomCat3、运行代码 1、下载Smart TomCat 由于小编的是社区版,没有自带的tomcat server,所以在设置的插件里面搜索,安装第一个(注意:安装时一定要关闭外网,小编因为这个…...
强化学习 DPO 算法:基于人类偏好,颠覆 PPO 传统策略
目录 一、引言二、强化学习基础回顾(一)策略(二)价值函数 三、近端策略优化(PPO)算法(一)算法原理(二)PPO 目标函数(三)代码示例&…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...