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 目标函数(三)代码示例&…...

长安链支撑全国不动产登记数据可信流通
转自人民日报客户端 不动产登记事关亿万企业、家庭的切身利益。促进不动产登记数据安全流通、业务高效协同,是各方持续努力的目标。记者1月7日从国家区块链技术创新中心获悉,我国自主可控、性能领先的区块链软硬件技术体系长安链,支撑自然资…...

GitCode 助力 Dora SSR:开启游戏开发新征程
项目仓库(点击阅读原文链接可直达) https://gitcode.com/ippclub/Dora-SSR 跨越技术藩篱,构建游戏开发乐园 Dora SSR 是一款致力于打破游戏开发技术壁垒的开源游戏引擎。其诞生源于开发者对简化跨平台游戏开发环境搭建的强烈渴望࿰…...

获取 Windows 视频时长的正确方式——Windows Shell API 深度解析
在 Qt 开发中,有时需要获取视频文件的时长,最直接的方法是在 Windows 上使用 Windows Shell API。然而,这涉及到 IShellItem、IPropertyStore 等 COM 组件,并需要正确处理 PKEY_Media_Duration。本篇文章将详细解析 Windows Shell API 获取视频时长的正确实现方式,并解决常…...

Linux系统安装Nginx详解(适用于CentOS 7)
目录 1. 更新系统包 2. 安装EPEL仓库 3. 安装Nginx 4. 启动Nginx服务 5. 设置Nginx开机自启 6. 检查Nginx状态 7. 配置防火墙 8. 访问Nginx默认页面 9. 配置Nginx(可选) 10. 重启Nginx 解决步骤 1. 检查系统版本 2. 移除错误的 Nginx 仓库 …...

深入理解Java对接DeepSeek
其实,整个对接过程很简单,就四步,获取key,找到接口文档,接口测试,代码对接。 1.获取 KEY https://platform.deepseek.com/transactions 直接付款就是了(现在官网暂停充值2025年2月7日…...

flutter isolate到底是啥
在 Flutter 中,Isolate 是一种实现多线程编程的机制,下面从概念、工作原理、使用场景、使用示例几个方面详细介绍: 概念 在 Dart 语言(Flutter 开发使用的编程语言)里,每个 Dart 程序至少运行在一个 Isol…...

深入剖析 Apache Shiro550 反序列化漏洞及复现
目录 前言 一、认识 Apache Shiro 二、反序列化漏洞:隐藏在数据转换中的风险 三、Shiro550 漏洞:会话管理中的致命缺陷 四、漏洞危害:如多米诺骨牌般的连锁反应 五、漏洞复现:揭开攻击的神秘面纱 (一࿰…...

计算机毕业设计——Springboot的简历系统
📘 博主小档案: 花花,一名来自世界500强的资深程序猿,毕业于国内知名985高校。 🔧 技术专长: 花花在深度学习任务中展现出卓越的能力,包括但不限于java、python等技术。近年来,花花更…...

【kubernetes组件合集】深入解析Kubernetes组件之三:client-go
深入解析Kubernetes组件之三:client-go 目录 深入解析Kubernetes组件之三:client-go 引言 1. client-go简介 2. client-go的功能 2.1 资源操作 2.2 资源监听 2.3 认证和授权 2.4 错误处理和重试 2.5 扩展性和定制化 3. 使用client-go与Kubern…...

线程池-抢票系统性能优化
文章目录 引言-购票系统线程池购票系统-线程池优化 池化 vs 未池化 引言-购票系统 public class App implements Runnable {private static int tickets 100;private static int users 10000;private final ReentrantLock lock new ReentrantLock(true);public void run() …...