C++初阶--C++入门(引用篇)

目录

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

  1.定义与声明

 2.特性

二、引用的进阶用法

1.函数参数传递:

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

引用作为返回值的优点

引用作为返回值的注意事项

代码示例

注意事项的进一步说明

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

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

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

四、 引用和指针的区别


在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/1560072.html

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

相关文章

自养号测评:亚马逊测评实操步骤与风险规避策略详解

尽管亚马逊平台明确表态不支持产品测评行为&#xff0c;卖家群体对于测评活动的需求却并未因此减退。这主要是因为&#xff0c;测评在增强产品曝光、推动销量上升及构建品牌信任度方面扮演了不可或缺的角色。在此情境下&#xff0c;即便测评伴随着一定的风险&#xff0c;卖家仍…

探索风能行业前景,博冠将携8K超高清风电场智能运维系统亮相CWP2024北京国际风能大会

当金秋的微风轻抚蔚蓝的天际&#xff0c;全球风电盛会——2024北京国际风能大会暨展览会&#xff08;CWP 2024&#xff09;&#xff0c;将于10月16日至18日在北京中国国际展览中心&#xff08;顺义馆&#xff09;盛大启幕。BOSMA博冠将携旗下全球首个8K超高清风电场智能巡检系统…

中国市场的NFT生存法则:消费属性与圈子文化

自2021年NFT数字藏品概念爆发以来&#xff0c;它迅速吸引了全球范围内的玩家、投资者以及艺术家和品牌的参与。然而&#xff0c;随着市场逐渐冷却&#xff0c;尤其是在中国市场&#xff0c;NFT的定位变得越来越微妙和复杂。在全球其他地区&#xff0c;NFT逐渐走向金融化&#x…

Unity3D相关知识点总结

Unity3D使用的是笛卡尔三维坐标系&#xff0c;并且是以左手坐标系进行展示的。 1.全局坐标系&#xff08;global&#xff09; 全局坐标系描述的是游戏对象在整个世界&#xff08;场景&#xff09;中的相对于坐标原点&#xff08;0&#xff0c;0&#xff0c;0&#xff09;的位置…

在线绘图工具drawio,visio的平替

Draw.io&#xff1a;灵活高效的在线绘图工具推荐 在工作和项目管理中&#xff0c;流程图、架构图和思维导图等可视化图表是非常重要的沟通工具。Draw.io&#xff08;现更名为diagrams.net&#xff09;是一个强大且免费的在线绘图工具&#xff0c;适用于创建各种类型的图表。它功…

激光slam学习笔记5--基于走直线标定RTK与车体旋转外参

背景&#xff1a;车子走直线&#xff0c;可以把RTK标定到车身&#xff0c;之前没有操作过&#xff0c;手推一下公式&#xff0c;发现也挺简单的。 一、证明过程 &#xff08;直接上操作&#xff0c;字错莫怪&#xff0c;嘻嘻&#xff09; 二、进一步解析 1&#xff09;通过…

选择三品软件作为合作伙伴,确保PLM系统成功实施和长期稳定运行

企业在初次实施PLM&#xff08;Product Lifecycle Management&#xff0c;产品生命周期管理&#xff09;系统时&#xff0c;需要细致规划和充分准备。 一、明确需求和目标 战略对齐&#xff1a;首先&#xff0c;企业需要明确PLM系统如何与企业的长期战略相匹配&#xff0c;比如…

腾讯云实时音视频 SDK(TRTC SDK)相关

实时音视频 SDK&#xff08;TRTC SDK&#xff09; 的 RoomID 是什么&#xff1f;取值区间值是多少&#xff1f; RoomID 即房间号&#xff0c;用于唯一标识一个房间。房间号取值区间为1 - 4294967295&#xff0c;由开发者自行维护和分配。 实时音视频 SDK&#xff08;TRTC SDK…

智慧园区平台项目建设方案

随着信息技术的飞速发展&#xff0c;智慧园区作为智慧城市的重要组成部分&#xff0c;正逐渐成为推动城市可持续发展的关键力量。本文旨在探讨智慧园区平台项目的建设内容&#xff0c;以期为相关领域的专家学者和决策者提供参考。 1. 智慧园区的定义与重要性 智慧园区是指运用…

直播预告 | 药品安全与合规保障难?智能温度监测助您领先制药工业4.0!

您是否在为温度敏感药品的运输和存储合规而苦恼&#xff1f; 是否担心冷链物流中的温度监控漏洞导致药品质量下降&#xff1f; 制药环境中的温湿度监控是否让您无从下手&#xff1f; 这些问题不仅影响药品的安全性&#xff0c;也直接影响企业的合规性和市场竞争力。如何确保环…

实验五 队列的应用

实验五 队列的应用 一、实验目的 1&#xff0e;掌握队列的顺序存储结构 2&#xff0e;掌握队列先进先出运算原则在解决实际问题中的应用 二、实验内容 1.仿照教材顺序循环队列的例子&#xff0c;设计一个只使用队头指针和计数器的顺序循环队列抽象数据类型。其中操作包括:初…

a-tree节点自定义内容(ant-design-vue 1.x版)

1.在树结构对应的数据中&#xff0c;给需要自定义层的数据添加scopedSlots属性 this.siteList [{name: "parent1",children: [{name: "children1",},],},{name: "parent2",children: [{name: "children1",},{name: "children2&…

TAGE-SC分支预测器

此文为学习昆明湖SC分支预测器的整理版本&#xff1b; SC分支预测器 SC&#xff08;Statistics counter&#xff09;分支预测器是一种基于历史统计信息的分支预测器。 与TAGE类似&#xff0c;在SC中通常有多个表Tn&#xff0c;他们对应了不同历史长度的跳转统计&#xff1b;同…

Java学习第九天

相同包下的类可以直接访问&#xff0c;不同包下的类需要导包才可以使用&#xff0c;导包格式&#xff1a;import 包名.类名 final关键字&#xff1a; 常量&#xff1a; 枚举&#xff1a;一种特殊的类型(反编译之后本质就是实例常量&#xff0c;自己定义的类&#xff0c;创建了几…

使用java做一个微信机器人

微信机器人这个功能&#xff0c;目前在市面上运用的还是不是很多&#xff0c;每个人实现机器人的目的也不一样&#xff0c;有的为了自动加好友;有的为了自动拉群:也有的为了机器人对话聊天等等一系列。想必大家对微信机器人感兴趣的伙伴&#xff0c;但是大多数走到一半遇到各种…

Linux-Docker阿里云镜像仓库失效

写在前面&#xff0c;这个是我很早之前在VmWare安装的Linux7,通过yum 安装的docker&#xff0c;但是今天怎么都无法pull镜像&#xff0c;报错如下。 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waitin…

达梦DBLINK访问ORACLE配置方法

目录 1、概述 2、测试环境 3、语法简介 4、配置访问DM的DBLINK 5、配置访问ORACLE的DBLINK 5.1 通过OCI配置 5.2 通过ODBC配置 1、概述 本文介绍了达梦DBLINK的配置方法。有3部分内容&#xff0c;1&#xff09;达梦访问到达梦的配置方法&#xff1b;2&#xff09;通过OC…

Android实现ViewPager剧中放大效果

效果图 实现方式核心思想是自定义PageTransformer继承ViewPager.PageTransformer&#xff0c;精确控制每一个page的动效。 PageTransformer的transformPage方法并不会区分当前的page是哪一个&#xff0c;所以需要我们自己去识别&#xff0c;我的方法是每个page添加一个text显…

【element-tiptap】如何引进系统中的字体?

源码地址&#xff1a; https://github.com/Leecason/element-tiptap 源码中给出的字体如下 可以看到&#xff0c;咱们日常需要的黑体、微软雅黑等都没有&#xff0c;所以这篇文章来探索一下怎么加字体。 另外呢&#xff0c;肯定有小伙伴发现&#xff0c;这个按钮点击的时候&am…

NVM下载、安装、配置

一、下载 nvm在win系统下载链接 https://github.com/coreybutler/nvm-windows/releases zip版本已上传到资源文件中&#xff0c;链接 https://download.csdn.net/download/m0_46613429/89870864 二、安装 1、双击exe文件 2、选择 accept 3、选择nvm安装位置 4、选择nodejs安…