当前位置: 首页 > news >正文

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进行操作。
  • 变量rara具有相同的地址。

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, 创建变量x
  • int& y = x, y是x的引用。此处, y是int型的引用,发生了权限的平移。
  • const int& z = x, 此处发生了权限的缩小。该内存块在使用名字z时,权限为const,不能修改
  • 名字x和y权限相同,即, 该内存块在使用名字xy时,可以修改
  • 因此++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. 总结引用要点

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

8. 最佳实践指南

  1. 优先const引用:函数参数尽量使用const T&形式
  2. 警惕返回引用:确保返回对象生命周期足够
  3. 替代输出参数:用引用替代指针作为输出参数
  4. 类型转换注意:隐式转换产生临时变量需用const引用
  5. 与智能指针配合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】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…...

github - 使用

注册账户以及创建仓库 要想使用github第一步当然是注册github账号了, github官网地址:https://github.com/。 之后就可以创建仓库了(免费用户只能建公共仓库),Create a New Repository,填好名称后Create,之后会出现一些仓库的配置信息,这也是一个git的简单教程。 Git…...

内网ip网段记录

1.介绍 常见的内网IP段有&#xff1a; A类&#xff1a; 10.0.0.0/8 大型企业内部网络&#xff08;如 AWS、阿里云&#xff09; 10.0.0.0 - 10.255.255.255 B类&#xff1a;172.16.0.0/12 中型企业、学校 172.16.0.0 - 172.31.255.255 C类&#xff1a;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的实现 案例&#xff1a;构建表达式树&#xff0c;简化值对象的比较 值对象优点 把有紧密关系的属性打包为一个类型把领域知识放到类的定义中 class shangjia {long id;string nam…...

【分布式理论9】分布式协同:分布式系统进程互斥与互斥算法

文章目录 一、互斥问题及分布式系统的特性二、分布式互斥算法1. 集中互斥算法调用流程优缺点 2. 基于许可的互斥算法&#xff08;Lamport 算法&#xff09;调用流程优缺点 3. 令牌环互斥算法调用流程优缺点 三、三种算法对比 在分布式系统中&#xff0c;多个应用服务可能会同时…...

木材表面缺陷检测数据集,支持YOLO+COCO JSON+PASICAL VOC XML+DARKNET格式标注信息,平均正确识别率95.0%

数据集说明 木材表面缺陷检测数据集是用于训练和验证人工智能算法&#xff0c;以帮助自动识别和检测木材表面的缺陷&#xff0c;如裂纹、疤痕、孔洞等。这对于木材行业非常重要&#xff0c;可以提高生产过程的效率和质量控制水平。 本文提供的木材表面缺陷检测数据集&#xff0…...

Leetcodehot 力扣热题100 二叉搜索树中第 K 小的元素

class Solution { public:int res; // 用于存储第 k 小的元素int kthSmallest(TreeNode* root, int k) {inorder(root, k); // 进行中序遍历并找到第 k 小的元素return res; // 返回结果}private:// 中序遍历&#xff1a;遍历树的左子树、根节点和右子树void inorder(TreeNod…...

Awtk 如何添加开机画面

场景 我们知道在工程中&#xff0c;Ui是一个线程&#xff0c;并且需要一直存在&#xff0c;当我们使用的开机画面在这个线程开启就直接展示的时候&#xff0c;因为awtk的界面是window_open入栈的&#xff0c;即首次打开的窗口会记录在top&#xff0c;往后的窗口会依次往后存放&…...

关于多语言商城系统的开发流程

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

IDEA中常见问题汇总

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…...

计算机视觉-拟合

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

CSS 实现下拉菜单效果实例解析

1. 引言 在 Web 开发过程中&#xff0c;下拉菜单是一种常见且十分实用的交互组件。很多前端教程都提供过简单的下拉菜单示例&#xff0c;本文将以一个简洁的实例为出发点&#xff0c;从 HTML 结构、CSS 样式以及整体交互逻辑三个层面进行详细解析&#xff0c;帮助大家理解纯 C…...

DeepSeek模拟阿里面试——Mysql

1.数据库基础知识 关系型数据库是什么&#xff1f; 关系型数据库是基于关系模型的数据库&#xff0c;使用表格来存储数据&#xff0c;表格之间可以通过键建立关系。 数据库的ACID特性是什么&#xff1f; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务要么全部完成…...

MVVM设计模式

‌MVVM&#xff08;Model-View-ViewModel&#xff09;是一种软件设计模式,MVVM模式由三个主要部分组成&#xff1a; ‌Model&#xff08;模型&#xff09;‌&#xff1a;负责管理应用程序的业务逻辑和数据。它不关心UI如何展示数据&#xff0c;主要负责与服务器通信和数据处处…...

解决: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. 输出 选取部分结果&#xff1a; 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 由于小编的是社区版&#xff0c;没有自带的tomcat server&#xff0c;所以在设置的插件里面搜索&#xff0c;安装第一个&#xff08;注意&#xff1a;安装时一定要关闭外网&#xff0c;小编因为这个…...

强化学习 DPO 算法:基于人类偏好,颠覆 PPO 传统策略

目录 一、引言二、强化学习基础回顾&#xff08;一&#xff09;策略&#xff08;二&#xff09;价值函数 三、近端策略优化&#xff08;PPO&#xff09;算法&#xff08;一&#xff09;算法原理&#xff08;二&#xff09;PPO 目标函数&#xff08;三&#xff09;代码示例&…...

长安链支撑全国不动产登记数据可信流通

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

GitCode 助力 Dora SSR:开启游戏开发新征程

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

获取 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&#xff08;可选&#xff09; 10. 重启Nginx 解决步骤 1. 检查系统版本 2. 移除错误的 Nginx 仓库 …...

深入理解Java对接DeepSeek

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

flutter isolate到底是啥

在 Flutter 中&#xff0c;Isolate 是一种实现多线程编程的机制&#xff0c;下面从概念、工作原理、使用场景、使用示例几个方面详细介绍&#xff1a; 概念 在 Dart 语言&#xff08;Flutter 开发使用的编程语言&#xff09;里&#xff0c;每个 Dart 程序至少运行在一个 Isol…...

深入剖析 Apache Shiro550 反序列化漏洞及复现

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

计算机毕业设计——Springboot的简历系统

&#x1f4d8; 博主小档案&#xff1a; 花花&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 花花在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于java、python等技术。近年来&#xff0c;花花更…...

【kubernetes组件合集】深入解析Kubernetes组件之三:client-go

深入解析Kubernetes组件之三&#xff1a;client-go 目录 深入解析Kubernetes组件之三&#xff1a;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() …...