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等同于修改a
    cout << 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>&管理资源(后续讲解)

以上就是关于引用的所有内容了,码字整理不易,欢迎各位大佬在评论区交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/967925.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网易易盾接入DeepSeek,数字内容安全“智”理能力全面升级

今年农历新年期间&#xff0c;全球AI领域再度掀起了一波革命性浪潮&#xff0c;国产通用大模型DeepSeek凭借其强大的多场景理解与内容生成能力迅速“出圈”&#xff0c;彻底改写全球人工智能产业的格局。 作为国内领先的数字内容风控服务商&#xff0c;网易易盾一直致力于探索…

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue旅游管理网站

开题报告 本论文探讨了一款采用现代Web开发技术构建的台州市旅游综合信息与服务平台的设计与实现。该系统基于SpringBoot框架&#xff0c;以其轻量级、快速开发和强大的企业级应用支持能力为核心后端技术支撑&#xff0c;结合Vue.js前端框架及ElementUI组件库&#xff0c;为用…

python-leetcode-寻找峰值

162. 寻找峰值 - 力扣&#xff08;LeetCode&#xff09; class Solution:def findPeakElement(self, nums: List[int]) -> int:left, right 0, len(nums) - 1while left < right:mid left (right - left) // 2if nums[mid] < nums[mid 1]:left mid 1else:right …

2.11学习记录

web——CTFHub XSS学习 学习资料&#xff1a;xss&#xff08;跨站攻击&#xff09; 原理 1.黑客发送带有xss恶意脚本的链接给用户 2.用户点击了恶意链接&#xff0c;访问了目标服务器&#xff08;正常的服务器&#xff09; 3.目标服务器&#xff08;正常的服务器&#xff09…

macOS 上部署 RAGFlow

在 macOS 上从源码部署 RAGFlow-0.14.1&#xff1a;详细指南 一、引言 RAGFlow 作为一款强大的工具&#xff0c;在人工智能领域应用广泛。本文将详细介绍如何在 macOS 系统上从源码部署 RAGFlow 0.14.1 版本&#xff0c;无论是开发人员进行项目实践&#xff0c;还是技术爱好者…

ASP.NET Core WebSocket、SignalR

目录 WebSocket SignalR SignalR的基本使用 WebSocket WebSocket基于TCP协议&#xff0c;支持二进制通信&#xff0c;双工通信。性能和并发能力更强。WebSocket独立于HTTP协议&#xff0c;不过我们一般仍然把WebSocket服务器端部署到Web服务器上&#xff0c;因为可以借助HT…

【蓝桥杯嵌入式】4_key:单击+长按+双击

全部代码网盘自取 链接&#xff1a;https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd3ii2 提取码&#xff1a;3ii2 1、电路图 将4个按键的引脚设置为input&#xff0c;并将初始状态设置为Pull-up&#xff08;上拉输入&#xff09; 为解决按键抖动的问题&#xff0c;我们…

五、AIGC大模型_01大模型基础知识

1、基本概念 1.1 定义 目前&#xff0c;谈到大模型&#xff0c;通常都指的是大语言模型&#xff08;LLMs&#xff0c;即&#xff1a;Large Language Models) 大语言模型是具有大规模参数和复杂计算结构的深度学习模型&#xff0c;通常由深度神经网络构建而成&#xff0c;参数…

微服务与网关

什么是网关 背景 单体项目中&#xff0c;前端只用访问指定的一个端口8080&#xff0c;就可以得到任何想要的数据 微服务项目中&#xff0c;ip是不断变化的&#xff0c;端口是多个的 解决方案&#xff1a;网关 网关&#xff1a;就是网络的关口&#xff0c;负责请求的路由、转发…

Spring Cloud工程完善

目录 完善订单服务 启动类 配置文件 实体类 Controller Service Mapper 测试运行 完成商品服务 启动类 配置文件 实体类 Controller Service Mapper 测试运行 远程调用 需求 实现 1.定义RestTemplate 2.修改order-service中的OrderService 测试运行 Rest…

网络安全网格架构(CSMA) 网络安全框架csf

CSRF:Cross Site Request Forgy&#xff08;跨站请求伪造&#xff09; 用户打开另外一个网站&#xff0c;可以对本网站进行操作或攻击。容易产生传播蠕虫。 CSRF攻击原理&#xff1a; 1、用户先登录A网站 2、A网站确认身份返回用户信息 3、B网站冒充用户信息而不是直接获取用…

数据库系统课设——教务管理系统

目录 前言 一、总体设计 1、知识背景 2、模块介绍&#xff08;需求分析&#xff09; 3、设计步骤 3.1 页面原型设计 3.2 前端页面开发 3.3 后端接口开发 3.4 数据库设计 二、详细设计 1、 系统功能模块划分 2、 数据流程图 3、数据库概念结构设计 4、 数据库逻辑…

论文概览 |《Cities》2024.12 Vol.155(上)

本次给大家整理的是《Cities》杂志2024年12月第152期的论文的题目和摘要&#xff0c;一共包括73篇SCI论文&#xff01;由于论文过多&#xff0c;我们将通过两篇文章进行介绍&#xff0c;本篇文章介绍第1--第30篇论文! 论文1 Digital economy and risk response: How the digita…

FANUC机器人示教器中如何显示或关闭寄存器或IO的注释信息?

FANUC机器人示教器中如何显示或关闭寄存器或IO的注释信息? 如下图所示,我们打开一个子程序,可以看到程序中的寄存器和IO是显示注释信息的, 如果想关闭注释显示的话,怎么设置? 如下图所示,按下下一页的箭头(NEXT键), 如下图所示,点击“编辑”,在弹出的窗口中,选择“…

[QMT量化交易小白入门]-二十二、deepseek+cline+vscode,让小白使用miniQMT量化交易成为可能

本专栏主要是介绍QMT的基础用法&#xff0c;常见函数&#xff0c;写策略的方法&#xff0c;也会分享一些量化交易的思路&#xff0c;大概会写100篇左右。 QMT的相关资料较少&#xff0c;在使用过程中不断的摸索&#xff0c;遇到了一些问题&#xff0c;记录下来和大家一起沟通&a…

快速集成DeepSeek到项目

DeepSeek API-KEY 获取 登录DeekSeek 官网&#xff0c;进入API 开放平台 2. 创建API-KEY 复制API-KEY进行保存&#xff0c;后期API调用使用 项目中集成DeepSeek 这里只展示部分核心代码&#xff0c;具体请查看源码orange-ai-deepseek-biz-starter Slf4j AllArgsConstructo…

关于浏览器缓存的思考

问题情境 开发中要实现一个非原生pdf预览功能&#xff0c;pdf链接放在一个固定的后台地址&#xff0c;当重新上传pdf后&#xff0c;预览pdf仍然是上一次的pdf内容&#xff0c;没有更新为最新的内容。 查看接口返回状态码为 200 OK(from disk cache)&#xff0c; 表示此次pdf返回…

MAAS | Ollama 搭建本地 AI 大模型 deepseekWeb 界面调用

目录 一、环境准备二、安装 Ollama三、下载并部署 DeepSeek 模型四、简单交互五、通过 Web 界面调用大模型 在当今人工智能快速发展的时代&#xff0c;本地部署大语言模型赋予了用户更高的灵活性和个性化服务体验。本文介绍了如何准备环境、安装Ollama框架、下载并部署DeepSeek…

C++ ——从C到C++

1、C的学习方法 &#xff08;1&#xff09;C知识点概念内容比较多&#xff0c;需要反复复习 &#xff08;2&#xff09;偏理论&#xff0c;有的内容不理解&#xff0c;可以先背下来&#xff0c;后续可能会理解更深 &#xff08;3&#xff09;学好编程要多练习&#xff0c;简…

Baklib助力内容中台实施最佳实践的关键要素与成功案例

内容概要 内容中台的实施对于现代企业在数字化转型过程中具有重要的战略意义。内容中台不仅提升内容管理的效率&#xff0c;还能为企业提供更灵活的内容运营能力。在实施过程中&#xff0c;关键在于了解如何构建有效的架构设计、选择适合的技术、以及促进团队协作。尤其是像Ba…