C++入门(引用篇)

在C++编程的广阔天地中,引用是一种强大且独特的工具,它允许程序员为已存在的变量创建别名,通过这个别名可以直接访问和操作原始变量。引用的这一特性不仅简化了代码,提高了代码的可读性,还带来了性能上的优势。接下来,我们将对C++中的引用进行深入剖析,探讨其工作机制、应用场景以及需要注意的事项,并通过具体代码示例进行说明。

一、引用的基本概念与特性

  1.定义与声明

  • 引用是C++中对某一变量(目标变量)的别名。通过引用,我们可以直接访问和操作原始变量,而无需通过指针的间接访问方式。
  • 在C++中,使用&符号来声明引用。其基本语法为:

        类型标识符& 引用名 = 已存在的变量名;

int main()
{int a = 10;int& ra = a;//ra 就是 a 的别名int& x = a;int& y = x;
}

 2.特性

  • 必须初始化:引用在声明时必须被初始化,且一旦初始化后,其引用的对象不能改变。即引用不能重新绑定到另一个变量上。
  • 不占内存:从概念上讲,引用本身不占用内存空间,因为它只是原始变量的一个别名。但在底层实现上,编译器通常会将引用实现为指向原始变量的常量指针(const pointer),因此实际上会占用指针大小的内存。
  • 类型一致:引用的类型必须与它所引用的变量的类型一致。

二、引用的进阶用法

1.函数参数传递

  • 通过引用传递函数参数可以避免数据的复制:做输出型参数,对象比较大时减少拷贝从而提高函数的执行效率。
void swap(int& x, int& y) {  int temp = x;  x = y;  y = temp;  
}  int main() {  int a = 5, b = 10;  swap(a, b);  std::cout << "a: " << a << ", b: " << b << std::endl; // 输出a: 10, b: 5  return 0;  
}

2.引用作为函数返回值(重点)

 

引用作为返回值的优点
  1. 避免拷贝:当函数返回大型对象或容器时,如果直接返回对象本身,会导致对象的拷贝。而返回引用则可以避免这种不必要的拷贝,从而提高效率。

  2. 允许修改:通过返回引用,调用者可以修改被返回对象的状态。这在某些情况下是非常有用的,比如当你需要实现一个返回容器中某个元素的函数时。

  3. 支持链式操作:返回引用允许实现链式操作,即连续调用返回引用的成员函数。

引用作为返回值的注意事项
  1. 确保对象存在:返回引用时,必须确保被引用的对象在函数返回后仍然有效。如果返回的是局部变量的引用,那么当函数结束时,局部变量会被销毁,返回的引用将指向一个无效的内存位置,这会导致未定义行为。

  2. 避免返回非常量引用:除非有充分的理由,否则应避免返回非常量引用,因为这可能会允许调用者修改被返回对象的状态,从而引入潜在的错误和不可预测的行为。如果确实需要返回可修改引用,应确保调用者明白这一点,并小心处理。

  3. 考虑使用常量引用:当不需要修改被返回对象时,应优先考虑返回常量引用。这不仅可以保护对象不被修改,还可以提高代码的可读性和安全性。

代码示例

下面是一个返回常量引用的示例,该示例从std::vector中查找并返回最大元素的引用(作为常量,因为不希望调用者修改它):

#include <iostream>  
#include <vector>  
#include <algorithm> // for std::max_element  const int& findMax(const std::vector<int>& vec) 
{// 使用std::max_element找到最大元素的迭代器  auto it = std::max_element(vec.begin(), vec.end());// 返回最大元素的引用(作为常量)  return *it;
}int main() 
{std::vector<int> nums = { 1, 3, 7, 2, 9, 5 };const int& maxNum = findMax(nums);std::cout << "The maximum number is " << maxNum << std::endl;// 注意:不能修改maxNum,因为它是常量引用  // maxNum = 100; // 这会导致编译错误  return 0;
}

 在这个例子中,findMax函数返回了一个常量引用,指向vector中的最大元素。由于返回的是常量引用,调用者不能修改被返回的元素。

注意事项的进一步说明
  • 避免返回局部变量的引用:如前所述,这是非常危险的,因为局部变量在函数结束时会被销毁。

  • 考虑对象的生命周期:返回引用时,应确保被引用的对象在整个程序运行期间都是有效的。例如,如果函数返回了一个指向堆上分配对象的引用,那么调用者必须负责在适当的时候释放该对象。

  • 使用智能指针:在某些情况下,使用智能指针(如std::shared_ptrstd::unique_ptr)作为返回值可能是一个更好的选择。这可以自动管理对象的生命周期,并避免悬垂引用的问题。然而,这也增加了代码的复杂性,并可能引入其他潜在的问题,如循环引用和性能开销。因此,在使用智能指针时应谨慎考虑。

 

三、传值和传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

1.值和引用的作为参数的性能比较

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{A a;// 以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2(a);size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}int main()
{TestRefAndValue();return 0;
}

 

 从运行时间可以看出,传引用的效率更高。

2.值和引用的作为返回值类型的性能比较 

#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;}int main()
{//TestRefAndValue();TestReturnByRefOrValue();return 0;
}

 

 通过上述代码的比较,我们可以发现传值和指针在作为传参以及返回值类型上效率相差很大。

四、 引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

指针和引用的功能是类似的,重叠的。

C++的引用,对指针使用比较复杂的场景进行一些替换,让代码更简单易懂,但是不能完全替代指针。

引用不能完全替代指针的原因:引用定义后,不能改变指向(在链表的实现中就需要经常改变指向)。

int main(){int a = 10;int& ra = a;cout<<"&a = "<<&a<<endl;cout<<"&ra = "<<&ra<<endl;return 0;}

 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

int main()
{int a = 10;int& ra = a;ra = 20;int* pa = &a;*pa = 20;return 0;
}

我们来看下引用和指针的汇编代码对比: 

总结:

引用和指针的不同点:

1、定义与基本概念

引用:

引用在C++等编程语言中是一个重要的概念,它相当于某个变量的别名。

引用必须在声明时被初始化,且一旦被初始化后,就不能再改变引用的对象(但对象的值可以改变)。

指针:

指针是一个变量,其存储的是另一个变量的内存地址。

指针可以在任何时候被初始化,且可以随时改变其指向的对象。

2、内存与访问方式

引用:

引用本身不占用内存空间,它只是对象的别名。

对引用的操作实际上是对原对象的直接操作。

指针:

指针本身占用一定的内存空间,用于存储地址信息。

通过指针访问对象时,需要先解引用(即使用“*”操作符),才能访问指针所指向的对象。

3、特性与安全性

引用:

引用不能为空,它必须与合法的存储单元关联。

引用是类型安全的,编译器会对引用进行类型检查。

由于引用不能改变指向的对象,因此它在一定程度上比指针更安全。

指针:

指针可以为空(即指向NULL),也可以指向非法的内存地址(野指针)。

指针的类型安全性不如引用,因为编译器不会对指针进行严格的类型检查。

指针的灵活性更高,但也更容易出错,因此在使用时需要更加小心。

4、使用场景与示例

引用:

引用常用于函数参数传递和返回值,以避免不必要的拷贝和提高效率。

示例:void swap(int &a, int &b),在这个函数中,a和b都是对实参的引用,交换它们的值实际上是在交换实参的值。

指针:

指针常用于动态内存分配、数组操作、链表等数据结构以及函数指针等高级用法。

示例:int *p = new int(10);,这里p是一个指向整数的指针,它指向了一个动态分配的整数对象。

5、汇编层面的实现

从汇编层面来看,引用和指针在实现上有一定的相似性。例如,在C++中,引用在底层通常是通过指针来实现的。但是,编译器会对引用进行特殊的处理,以确保其安全性和类型安全性。因此,尽管引用和指针在汇编层面可能有一定的相似性,但在高级语言层面,它们的使用方式和特性是有显著区别的。

综上所述,引用和指针在编程中各有其独特的特性和使用场景。理解它们的区别和联系对于编写高效、安全的代码至关重要。 


 如有错误之处,望评论区指正

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

推理攻击-Python案例

1、本文通过推理攻击的方式来估计训练集中每个类别的样本数量、某样本是否在训练集中。 2、一种简单的实现方法&#xff1a;用模型对训练数据标签进行拟合&#xff0c;拟合结果即推理为训练集中的情况。 3、了解这些案例可以帮助我们更好的保护数据隐私。 推理攻击&#xff08;…

【Conda】Conda命令详解:高效更新与环境管理指南

目录 1. Conda 更新命令1.1 更新 Conda 核心1.2 更新所有包 2. 严格频道优先级3. 强制安装特定版本4. 创建与管理环境4.1 创建新环境4.2 激活和停用环境4.3 导出和导入环境4.4 删除环境 5. 清理缓存总结 Conda 是一个强大的包管理和环境管理工具&#xff0c;广泛应用于数据科学…

.net8系列-07图文并茂手把手教你连接SqlServer数据库使用log4net记录.net日志

文章目录 前情提要步骤概览 下载依赖下载安装成功 数据库准备脚本准备执行脚本&#xff0c;创建所需数据库创建成功&#xff0c;查看日志表 准备代码初始代码配置数据库开启数据库写入日志逻辑开启日志 运行测试删除之前的编译文件重新编译运行测试本地日志测试成功数据库日志测…

【英语】2. 英语的表达习惯

文章目录 前言less v. more n.解释e.g. less v. more prep.被动与中文的歧义总结参考文献 前言 进行英语前后缀的复习 less v. more n. 解释 外国的表达方式&#xff1a;更多地偏向静态&#xff0c;因此更多地使用名词 e.g. (rather Chinglish expression) She could not c…

使用 docker-compose 启动 es 集群 + kibana

编写 docker-compose yaml version: v3 services:elasticsearch-node1:image: elasticsearch:7.17.24container_name: elasticsearch-node1ports:- "9200:9200"- "9300:9300"environment:- node.nameelasticsearch-node1- cluster.namemy-es-cluster- dis…

云计算身份认证与访问控制(Cloud Computing Identity Authentication and Access Control)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

PyEcharts教程(002):上手PyEcharts

2、上手PyEcharts&#xff08;以jupyter notebook编译&#xff09; 2.1 如何查看pyecharts版本 import pyecharts print(pyecharts.__version__)2.2 上手Pyecharts 首先绘制第一个图表 from pyecharts.charts import Bar # 创建柱形图对象 bar Bar() # 添加x轴 bar.add_xa…

Python案例--九九乘法表

乘法口诀表是学习基础数学中不可或缺的工具&#xff0c;它帮助我们快速记忆乘法结果。在这篇文章中&#xff0c;我将向你展示如何使用Python编程语言来生成一个9x9的乘法口诀表。这不仅对教育工作者和学生有用&#xff0c;而且对任何需要快速回顾乘法事实的人来说都是一个有用的…

浸没边界 直接强迫法 圆球绕流验证 阅读笔记

Combined multi-direct forcing and immersed boundary method for simulating flows with moving particles https://doi.org/10.1016/j.ijmultiphaseflow.2007.10.004 他的意思是&#xff0c;不止需要一次的直接强迫 直接强迫的次数与误差成低于二阶的关系 不知道是不是一阶…

学习使用Cube软件

一、点亮LED灯 1、新建项目 File → New → STM32 Project搜索芯片信号项目名称 弹窗点击Yes 2、点亮LED 配置GPIO为输出模式 细化配置 保存&#xff08;ctrl S&#xff09;自动生成代码 手动生成代码 选择跳转到代码页面

【机器学习】知识总结1(人工智能、机器学习、深度学习、贝叶斯、回归分析)

目录 一、机器学习、深度学习 1.人工智能 1.1人工智能概念 1.2人工智能的主要研究内容与应用领域 1.2.1主要研究内容&#xff1a; 1.2.2应用领域 2.机器学习 2.1机器学习的概念 2.2机器学习的基本思路 2.3机器学习的分类 3.深度学习 3.1深度学习的概念 3.2人工智能…

网站集群批量管理-Ansible-模块管理

1. 概述 1. 自动化运维: 批量管理,批量分发,批量执行,维护 2. 无客户端,基于ssh进行管理与维护 2. 环境准备 环境主机ansible10.0.0.7(管理节点)nfs01 10.0.0.31(被管理节点)backup10.0.0.41(被管理节点) 2.1 创建密钥认证 安装sshpass yum install -y sshpass #!/bin/bash ##…

毕设 大数据抖音短视频数据分析与可视化(源码)

文章目录 0 前言1 课题背景2 数据清洗3 数据可视化地区-用户观看时间分界线每周观看观看路径发布地点视频时长整体点赞、完播 4 进阶分析相关性分析留存率 5 深度分析客户价值判断 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕…

自然语言处理问答系统

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

调试意义、步骤及方式

调试 文章目录 调试1.什么是bug&#xff1f;2.调试是什么&#xff1f;有多重要&#xff1f;2.1调试2.2调试的基本步骤2.3Debug和Release的介绍2.3.1常用的快捷键及功能1.F52.F9条件断点3.F104.F115.CTRLF5 3.调试的时候查看程序当前信息3.1查看临时变量的值3.1.1自动窗口3.1.2监…

力扣59.螺旋矩阵||

题目链接&#xff1a;59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff…

Github 2024-10-06 php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Blade项目2Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar数量:75969 个Fork数量:24281 次…

HarmonyOS第一课 04 应用程序框架基础-习题分析

判断题 1.在基于Stage模型开发的应用项目中都存在一个app.json5配置文件、以及一个或多个module.json5配置文件。T 正确(True) 错误(False) 这个答案是T - AppScope > app.json5&#xff1a;app.json5配置文件&#xff0c;用于声明应用的全局配置信息&#xff0c;比如应用…

【TypeScript】知识点梳理(三)

#void前面提到了代表空&#xff0c;但有个特殊情况&#xff0c;是空不是空&#xff0c;细谈是取舍&#xff0c;但我们不深究hhh# 代码示例&#xff1a; type func () > voidconst f1: func function() {return true; } 定义了空&#xff0c;返回非空值&#xff0c;理论…

关于软件开发的ChatGPT提示词

面向软件开发人员的 ChatGPT 提示词 不会用ChatGPT就如同上图~~ 所以先推荐一波好用的提示词 1. 代码生成&#xff1a; 为名为 [name] 的 [class/module/component] 生成具有以下功能的样板 [language] 代码&#xff1a;[functionality description]。 创建一个 [语言] 函数以…