C++之从C过渡(下)

news2024/11/15 22:27:29

C++之从C过渡(下)

在这里插入图片描述

接着上一篇,从引用开始往下讲解。

引用的特性

  • 引⽤在定义时必须初始化
  • ⼀个变量可以有多个引⽤
  • 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体

C++的引用不能完全替代指针。比如,在链表结点中我们会存储指向下一个结点的指针,而这个地方只能存指针,而不能存引用。这是因为C++的引用是不能改变指向的,也就是说我们要删除一个结点时,无法做到让前一个结点prev指向后一个结点next:

引用只是让一些原本使用指针的场景更简单。指针和引用是相辅相成的。

引用的使用

  • 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象
  • 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
  • 引⽤返回值的场景相对⽐较复杂,以后继续深⼊讲解。
  • 引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的点,C++引⽤定义后不能改变指向, Java的引⽤可以改变指向。
  • ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针。
引用作返回值

我们知道传值返回和传值传参是一样的,都是拷贝给临时对象,临时对象再作为返回值。而临时对象有一个特性:临时对象具有常性。所以:

int STTop(ST& rs)
{
    assert(rs.top>0);
    return rs.a[rs.top-1];
}

STTop(st1)=3;//"="左操作数必须为左值

我们不能通过这种方式去修改栈顶的元素。返回的是栈顶的临时拷贝,无法修改,相当于左侧是一个常量不能修改。

int& STTop(ST& rs)
{
    assert(rs.top>0);
    return rs.a[rs.top-1];
}

STTop(st1)=3;//现在就可以了

引用作返回值,返回的是引用或者说别名。中间就没有产生临时对象。所以就可以修改。引用作返回值的场景以后会很常见,很好用。

那么我们可以全部使用引用返回,不使用传值返回了吗?

不行。因为会产生类似野指针一样的东西:

int& func()
{
    int a=0;
    return a;
}

我们返回a的别名,但是a出了这个函数栈帧就已经销毁了。在底层我们返回这个别名也就是返回了这块空间的地址,所以就是野指针

而前面的:

int& STTop(ST& rs)
{
    assert(rs.top>0);
    return rs.a[rs.top-1];
}

这个返回的对象是在上的,出了这个函数这块空间还是在的,没有销毁。

const引用

  • 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。

  • 需要注意的是类似int& rb = a*3; double d = 12.34; int& rd = d;这样⼀些场景下a*3的和结果保存在⼀个临时对象中, int& rd = d; 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是说,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以

  • 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象, C++中把这个未命名对象叫做临时对象

const引用可以给常量、表达式取别名

(权限放大和缩小只存在于引用和指针,普通变量不存在权限的放大和缩小。)

int main()
{
    const int a = 10;//修改权限是只能读不能写
    //int& ra = a;//不能这样取别名,因为这样就权限放大了。
    const int& ra = a;//这样就可以
    
    int b = 20;
    const int& rb = b;//权限缩小,是可以的。只读不修改。不影响原本b的权限。缩小的不是原本的权限而是引用本身的权限。
    b++;//可以
    rb++;//不行
    
    int& rc=30;//普通的引用不能给30取别名
    const int& rc=30;//const引用可以给30取别名!
    
    int& rd=(a+b);//临时对象具有常性,触发了权限放大
    const int& rd=(a+b);//这样就可以
    int rd=(a+b);//而这一句是将临时对象的结果拷贝给变量rd,和权限无关
    
    return 0;
}

为了防止混淆,我们单独来看这几句:

const int a = 10;
int& ra = a;//这是权限放大
int rd = a;//这和权限没关系,只是拷贝。rd有自己的空间,修改rd的值和a没有关系。
double d = 12.34;
int i = d;//隐式类型转换,会用临时变量进行存储
int& ri = d;//不可以
const int& ri = d;//可以

类型转换时会产生临时对象(给double类型取double类型的别名不会产生临时变量),所以ri引用的不是d而是临时对象,而临时对象具有常性,所以用const就可以。

总结会产生临时对象的地方:表达式相加等(总之,表达式运算的结果)(提升或者截断);调用一个函数,函数传值返回;类型转换

在const引用临时对象后临时对象的生命周期就会跟着引用走,引用销毁了临时对象才会销毁

重点在于const引用的价值,这在以后会用到!

指针和引用的关系

C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。

语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

• 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。

• 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。

• 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。

• sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)

• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

inline

• ⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就不需要建⽴栈帧了,就可以提⾼效率

• inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。(否则可执行程序会变得太大)

• C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调试,C++设计了inline⽬的就是替代C的宏函数。(写宏真的很容易错)

• vs编译器debug版本下⾯默认是不展开inline的,这样⽅便调试,debug版本想展开需要设置⼀下以下两个地⽅。

• inline不建议声明和定义分离到两个⽂件(不是不建议分离),分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。所以直接定义到.h文件中。

nullptr

(C++11之后才添加的)

NULL实际是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码:

• C++中NULL可能被定义为字⾯常量0,或者C中被定义为⽆类型指针(void)的常量。不论采取何种定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦,本想通过f(NULL)调⽤指针版本的 f(int)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。f((void)NULL); 调⽤会报错。

#include<iostream>
using namespace std;

void f(int x)
{
 	 cout << "f(int x)" << endl;
}

void f(int* ptr)
{
	 cout << "f(int* ptr)" << endl;
}

int main()
{
	 f(0);
	 f(NULL);//想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。
 	 
	 f((void*)NULL);//传给两个都不匹配
    
 	 f(nullptr);
 	 return 0;
}

补充一个C和C++小差异:
//这段代码在C中可以,在C++中不行(更严格)
void* p1=NULL;
int* p2=p1;

所以C语言将NULL定义成(void*)0,在纯C代码中倒是问题不大。

• C++11中引⼊nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数****类型。

int* p2=nullptr;//可以
int i=nullptr;//不行

所以在C++尽量不要使用NULL了,有隐患。C++的NULL就是0,整型。

本文结束,感谢阅读=_=

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

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

相关文章

2024下半年EI收录的老牌会议,检索超快!

在科研领域&#xff0c;EI作为全球公认的工程技术领域重要检索工具&#xff0c;其收录的会议论文往往代表着某一领域内的最新研究成果与前沿技术。对于广大科研工作者而言&#xff0c;能够在EI收录的老牌会议上发表论文&#xff0c;不仅是对自身研究能力的一种肯定&#xff0c;…

pinctrl子系统做功能的切换.

SD卡和debug口中sdmmc和uart共用同一组pin脚,需实现在sd使用的时候切换到sdmmc不插入sd卡的时候使用debug口功能。 sd卡有检测脚可以作为切换的标志所以我们的切换要在sd卡的驱动中去做。 第一步&#xff1a; 使能俩个功能的dts并去除不能切换的pinctrl&#xff0c;只有一个节点…

自动回复的AI小助手,人工智能还是人工智障

最近在运营公司的百家号账号。因为老杨和同事们在一些大会上有干货满满的演讲&#xff0c;我们将它剪辑成比较短的视频&#xff0c;放在一些平台上供大家观看。百家号因百度的关系&#xff0c;搜索的引流会好一些。 一开始每次发好视频&#xff0c;就会有播放量。几次之后&…

Java每日一题———删除有序数组中的重复项

这个问题可以通过使用双指针技术来解决。我们可以使用两个指针&#xff0c;一个慢指针 slowRunner 用于跟踪新数组的末尾&#xff0c;另一个快指针 fastRunner 用于遍历数组。每当 fastRunner 遇到一个新的唯一元素时&#xff0c;就将其复制到 slowRunner 指向的位置&#xff0…

创建谷歌外链的常见错误及避免方法!

创建谷歌外链是个技术活&#xff0c;很多人在这个过程中容易犯错。了解这些常见错误和如何避免它们可以帮助你更有效地提升你的SEO表现。 其一&#xff0c;忽视锚文本多样性。有些人在建立外链时&#xff0c;总是使用相同的锚文本&#xff0c;这看起来很不自然&#xff0c;可能…

基于python爬虫技术的bilibili网用户数据采集系统的设计与实现-计算机毕业设计源码55962

摘要 在当今信息爆炸的时代&#xff0c;互联网已经成为人们获取信息、交流思想的重要平台。作为国内领先的弹幕视频网站&#xff0c;Bilibili凭借其独特的弹幕文化和丰富的内容生态&#xff0c;吸引了亿万用户的关注。这些用户生成的海量数据蕴含着丰富的信息&#xff0c;对于理…

异常(Java)

目录 1. 异常的概念 2. 异常的分类 3. 异常的处理 4. 异常的抛出 5. 异常的捕获 5.1 异常声明throws 5.2 try-catch捕获并处理 5.3 finally 6. 异常的处理流程 7. 自定义异常类 1. 异常的概念 异常就是在程序执行过程中发生的不正常的行为.异常中断了正在执行程序的…

Cross-Modality Person Re-identification with Memory-Based Contrastive Embedding

文章目录 题目&#xff1a;Cross-Modality Person Re-identification with Memory-Based Contrastive Embedding&#xff08;基于记忆对比嵌入的跨模态人物再识别&#xff09;摘要论文分析网络框架1、Problem Definition&#xff08;模态预处理&#xff09;2、Learning Modalit…

RUM技术探索:前端监控数据采集与实践

​​随着互联网技术的不断演进&#xff0c;Web应用程序正日益呈现出复杂多变与高度动态性的特征。用户渴望获得快速的页面加载、流畅的交互体验以及高度的可靠性。为了满足这些&#xff0c;实时监控 Web 应用的性能和行为变得至关重要。前端监控让开发者能够深入了解应用的表现…

Hack The Box-Resource

总体思路 phar反序列化->SSH CA私钥泄露->SSH CA私钥滥用->SSH脚本滥用 信息收集&端口利用 nmap -sSVC itrc.ssg.htb目标开放了两个ssh端口和一个80端口&#xff0c;先查看80端口 网站是一个SSG IT资源中心&#xff0c;主要用于解决网站问题、管理 SSH 访问、清…

免费【2024】springboot 付费自习室管理系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

正点原子imx6ull-mini-Linux驱动之Linux 音频驱动实验

虽然mini板子没有这个资源&#xff0c;但是可以学学移植这个软件。 音频是我们最常用到的功能&#xff0c;音频也是 linux 和安卓的重点应用场合。I.MX6ULL 带有 SAI 接口&#xff0c;正点原子的 I.MX6ULL ALPHA 开发板通过此接口外接了一个 WM8960 音频 DAC 芯片&#xff0c;…

《程序猿入职必会(10) · SpringBoot3 整合 MyBatis-Plus》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

使用samba在ubuntu和windows之间共享文件

1、在ubuntu上安装samba 在终端输入命令 sudo apt update sudo apt install samba 2、配置samba 打开samba 的配置文件 sudo nano /etc/samba/smb.conf 在文件末尾添加以下内容 [shared] path /home/lzx available yes valid users lzx read only no browsable yes…

【Redis进阶】Redis的持久化RDB和AOF

目录 持久化 RDB持久化 概念 原理 RDB 持久化的详细工作流程 1触发持久化&#xff1a; 2创建子进程&#xff1a; 3数据写入 RDB 文件&#xff1a; 4替换旧文件&#xff1a; 5回收子进程&#xff1a; RDB持久化的触发方式 1.手动触发&#xff1a; 2.自动触发&#…

鸿蒙应用服务开发【获取天气数据】

获取天气数据 介绍 Weather Service Kit&#xff08;天气服务&#xff09;是鸿蒙生态下的一个数据提供服务&#xff0c; Weather Service Kit融合了多家气象行业TOPs供应商&#xff0c;提供专业、精准、稳定的超本地化天气数据服务&#xff0c; 开发者可以通过Weather Servic…

Tomcat 漏洞

1.CVE-2017-12615 抓包&#xff0c;将get改为put jsp文件后加/ 访问木马使用蚁剑连接 2.弱口令 点击后输入默认用户名、密码&#xff1a;tomcat/tomcat 登录成功&#xff0c;在文件上传位置上传war包 使用哥斯拉生成一个jsp木马&#xff0c;打包&#xff0c;改后缀为war,上传…

如何有效防御短信接口遭受恶意攻击?

短信接口若遭遇恶意攻击&#xff0c;不仅加剧企业运营成本&#xff0c;更将严重损害企业形象。为有效预防此类风险&#xff0c;以下策略值得采纳&#xff1a; 1.设定合理的发送间隔&#xff1a;针对同一手机号码&#xff0c;设定合理的重复发送短信时间间隔&#xff0c;建议范…

Unity动态修改按钮点击效果

动态修改按钮色块&#xff0c;达到保留选中效果。 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class Demo: MonoBehaviour {private Button _frontBtn;private Button _backBtn;public ColorBlock NormalC…