初识c++(引用,inline,nullprt)

一、引用

1、定义

引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间

它和它引用的变量共用同⼀块内存空间。

类型& 引用别名 = 引用对象;

#include<iostream>
using namespace std;
int main()
{int a = 0;// 引⽤:b和c是a的别名int& b = a;int& c = a;// 也可以给别名b取别名,d相当于还是a的别名int& d = b;++d;// 这⾥取地址我们看到是⼀样的cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;return 0;
}

示例图片:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzlXcNLz-1720530489867)(https://i-blog.csdnimg.cn/direct/9193e7c2a59d4a00aba795ae80f65b43.png)]

2、特性

1、引用在定义时必须初始化

2、一个变量可以有多个引用

3、引用一旦引用一个实体,再不能引用其他实体(这于c的指针不同,指针可以改变指向的空间,而引用不行)。

#include<iostream>
using namespace std;
int main()
{int a = 10;// 编译报错:“ra”: 必须初始化引⽤//int& ra;int& b = a;int c = 20;// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,// 这⾥是⼀个赋值b = c;cout << &a << endl;cout << &b << endl;cout << &c << endl;return 0;
}

3、使用

引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被
引用对象。

void Swap(int* rx, int* ry)
{int tmp = *rx;*rx = *ry;*ry = tmp;
}
int main()
{int x = 0, y = 1;cout << x << " " << y << endl;Swap(&x, &y);cout << x << " " << y << endl;return 0;
}

我们在没有学习c++时实现两个数据的交换是通过指针方式来实现的,而我们知道形参是实参的一份临时拷贝,在调用函数时就会经行拷贝从而减少效率。但是,如果是c++用引用的话,rx,ry就是x,y不用再经行拷贝。

void Swap(int* rx, int* ry)
{int tmp = *rx;*rx = *ry;*ry = tmp;
}
int main()
{int x = 0, y = 1;cout << x << " " << y << endl;Swap(&x, &y);cout << x << " " << y << endl;return 0;
}

同时如果我们再日常使用中碰见修改栈顶元素的值时,我们没有学c++时我们会这么做(top为指向栈顶空间的下一个元素)。

void STModityTop(ST& rs, int x)
{rs.a[rs.top-1] = x;
}

但是我们可以直接将top-1的数据的引用作为返回值,返回到主函数经行修改。

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

这里要格外注意一点:引用不能引用函数中局部变量的数据,会造成类似野指针的问题。

4、const引用

​ 可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访

问权限在引用过程中可以缩小,但是不能放大。

不过需要注意的是类似 int& rb = a3; double d = 12.34; int& rd = d; 这样⼀些场景下a3的和结果保

存在一个临时对象中, int& rd = d 也是类似,在类型转换中会产生临时对象存储中间值,也就是说,

rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常

引用才可以。

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

把这个未命名对象叫做临时对象。(临时变量具有常性)

在 C++ 中,临时变量通常会在以下几个情况下创建:

1、函数返回值:当函数返回一个简单类型(如 int、float 等),如果没有显式指定返回类型,编译器会隐式

地创建一个临时局部变量来存储返回值。

2、参数传递:当函数接受简单类型的参数时,实参和形参之间的值会通过拷贝构造函数复制给临时变量。如

果是引用或指针,则不会创建新的临时变量。

3、表达式求值:在算术表达式、逻辑表达式等计算过程中,可能会创建临时变量来存储中间结果。

4、赋值操作:在赋值语句 a = b 中,如果 a 和 b 类型不匹配或其中一个为常量,系统会生成临时变量用于

交换它们的值。

5、括号展开:当使用括号改变运算顺序时,例如 (a + b) * c,会创建临时变量来存储 (a + b) 的结果。

6、强制类型转化。强转的结果会放在一个临时变量中。

int main()
{const int a = 10;// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”// 这⾥的引⽤是对a访问权限的放⼤//int& ra = a;// 这样才可以const int& ra = a;// 编译报错:error C3892: “ra”: 不能给常量赋值//ra++;// 这⾥的引⽤是对b访问权限的缩⼩int b = 20;const int& rb = b;// 编译报错:error C3892: “rb”: 不能给常量赋值//rb++;return 0;
}
#include<iostream>
using namespace std;
int main()
{int a = 10;const int& ra = 30;// 编译报错: “初始化”: ⽆法从“int”转换为“int &”// int& rb = a * 3;const int& rb = a*3;double d = 12.34;// 编译报错:“初始化”: ⽆法从“double”转换为“int &”// int& rd = d;const int& rd = d;return 0;
}

5、引用和指针的关系

C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功

能有重叠性,但是自己的特点,互相不可替代。

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

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

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

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

• sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下

占4个字节,64位下是8byte)

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

补:引用的底层是指针,但是我们理解的时候不能按照指针取理解,要分开去理解:下面的反汇编语言

说明了为什么引用底层是指针:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfPpuIxS-1720530489870)(https://i-blog.csdnimg.cn/direct/631c74346fb34131bf6b32c0fc9a02ef.png)]

可见两者反汇编语言是一模一样的。

二、inline

• 用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联

函数就需要建立栈帧了,就可以提高效率。

• inline对于编译器而言只是⼀个建议,也就是说,你加了inline编译器也可以选择在调用的地方不展

开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适用于频繁

调用的短小函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。

• vs编译器 debug版本下面默认是不展开inline的,这样方便调试,debug版本想展开需要设置⼀下

以下两个地方。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7ejOcw4-1720530489871)(https://i-blog.csdnimg.cn/direct/c22946899f8f49209961f0d1126c0748.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lWJd46yt-1720530489871)(https://i-blog.csdnimg.cn/direct/2f53c62f61584bde97021cdacea4ef44.png)]

• inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地

址,链接时会出现报错。

如果想观察是否展开可以看反汇编下是否有call指令即可,这里不再演示。

#include<iostream>
using namespace std;
inline int Add(int x, int y)
{int ret = x + y;ret += 1;ret += 1;ret += 1;return ret;
}
int main()
{// 可以通过汇编观察程序是否展开// 有call Add语句就是没有展开,没有就是展开了int ret = Add(1, 2);cout << Add(1, 2) * 5 << endl;return 0;
}

三、nullptr

我们先看看c语言中定义的宏:

#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif

按照这个我们在c++中写出下面的代码:

#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(int*)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。f(NULL);return 0;
}

我们可以发现两个都为第一个函数,这是因为在c语言中 NULL可以自动强转为任何类型的指针,所以本质上我们在使用malloc时不手动强制转化也是可以的,而c++不行:

#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(int*)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。f(NULL);f((int*)NULL);// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型// f((void*)NULL);return 0;
}

• C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。不论采取何种

定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过f(NULL)调用指针版本的

f(int)函数,但是由于NULL被定义成0,调用了f(int x),因此与程序的初衷相悖。f((void)NULL);*

调用会报错。

• C++11中引入nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字面量,它可以转换

成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被

隐式地转换为指针类型,而不能被转换为整数类型。

#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(int*)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。f(NULL);f((int*)NULL);// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型// f((void*)NULL);f(nullptr);return 0;
}
#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(int*)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。f(NULL);f((int*)NULL);// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型// f((void*)NULL);f(nullptr);return 0;
}

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

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

相关文章

342. 4的幂

哈喽&#xff01;大家好&#xff0c;我是奇哥&#xff0c;一位专门给面试官添堵的职业面试员 文章持续更新&#xff0c;可以微信搜索【小奇JAVA面试】第一时间阅读&#xff0c;回复【资料】更有我为大家准备的福利哟&#xff01; 文章目录 一、题目二、答案三、总结 一、题目 …

python--del

在Python中&#xff0c;del是一个关键字&#xff0c;用于删除对象。当你想删除列表的某个元素或者整个变量时&#xff0c;可以使用del。 以下是使用del的一些示例&#xff1a; 删除列表中的特定索引处的元素&#xff1a; my_list [1, 2, 3, 4, 5] del my_list[2] # 删除索引为…

C++ 算法——二分查找

如果要你在一个升序序列中查找一个值的位置&#xff0c;你是否还会傻乎乎的用下面这个 O ( n ) \mathcal O(n) O(n) 的代码暴力查找&#xff0c;如果是&#xff0c;我告诉你&#xff0c;其实根本不用这么做。 int find(int a[],int n,int k) {for(int i0;i<n;i) if(a[i]k)…

在Spring Boot项目中集成监控与报警

在Spring Boot项目中集成监控与报警 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 1. 引言 在当今的软件开发中&#xff0c;监控和报警系统是保证系统稳定性和可靠性的重要组成部分。Spring Boot…

华为机考真题 -- 篮球游戏

题目描述: 幼儿园里有一个放倒的圆桶,它是一个线性结构,只能在桶的右边将放入篮球,但是可以在桶的左边或者右边将取出篮球。每个篮球有单独的编号,老师可以一次性放入一个或者多个篮球,小朋友可以在桶左边或者右边取出篮球,当桶里只有一个篮球的情况下,只能从桶的左边…

视频监控技术在食品安全监管中的关键应用

视频监控技术在食品安全监管中的关键应用 1、视频监控技术在食品安全监管中的作用 在食品安全监管中&#xff0c;视频监控技术发挥着不可替代的作用。通过安装视频监控系统&#xff0c;可以实现对食品生产、运输、储存等各个环节的实时监控和录像存储。这不仅有助于监管部门及…

Linux的前世今生

Unix的起源和发展 1969年&#xff0c;AT&T贝尔实验室的Ken Thompson和Dennis Ritchie等人开发了Unix操作系统。Unix的设计理念强调小而简洁的工具&#xff0c;文本流和系统模块化&#xff0c;这些理念后来成为Linux开发的重要基础。1973年&#xff0c;Unix用C语言重新编写…

深度学习-数学基础(四)

深度学习数学基础 数学基础线性代数-标量和向量线性代数-向量运算向量加和向量内积向量夹角余弦值 线性代数-矩阵矩阵加法矩阵乘法矩阵点乘矩阵计算的其他内容 人工智能-矩阵的操作矩阵转置&#xff08;transpose&#xff09;矩阵与向量的转化 线性代数-张量&#xff08;tensor…

GEE代码实例教程详解:湖泊面积变化分析

GEE代码实例教程详解&#xff1a;湖泊面积变化分析 简介 在本篇博客中&#xff0c;我们将通过Google Earth Engine (GEE) 探索湖泊面积随时间的变化。通过分析MODIS数据集中的归一化差异水体指数&#xff08;NDWI&#xff09;&#xff0c;我们可以识别湖泊区域并监测其面积变…

达梦数据库kill会话

达梦数据库kill会话 在达梦数据库&#xff08;DM Database&#xff09;中&#xff0c;可以使用 SP_CLOSE_SESSION 存储过程来终止会话。这个存储过程需要提供会话 ID (sid) 作为参数&#xff0c;用于指定哪个会话需要被终止。 下面是使用 SP_CLOSE_SESSION 存储过程的详细步骤…

第5章 Vite高级功能(二)

文章目录 6 缓存和持久化6.1 配置缓存目录 7 压缩与最小化7.1 启用压缩7.2 配置压缩选项 8 生产环境优化8.1 移除调试信息8.2 配置环境变量 9 静态资源优化9.1 压缩图像9.2 合并和压缩 CSS9.3 使用 HTTP/2 10 分析构建结果10.1 使用分析插件10.2 生成包分析报告 6 缓存和持久化…

PostgreSQL的使用

PostgreSQL的使用 1.首先&#xff0c;使用docker进行安装pgvector数据库&#xff0c;具体的安装步骤可以查看我之前发的博文。 2.docker exec -it pgvector /bin/bash 进入docker容器内部&#xff0c;操作数据库&#xff0c;上述命令是以交互式命令进入了容器的内部&#xf…

卷技术还是卷应用?李彦宏给出了明确答案

如何理解李彦宏说的“不要卷模型&#xff0c;要卷应用” 引言 7月4日&#xff0c;2024世界人工智能大会在上海世博中心召开。百度创始人兼CEO李彦宏在产业发展主论坛上呼吁&#xff1a;“大家不要卷模型&#xff0c;要卷应用&#xff01;”这句话引起了广泛讨论。李彦宏认为&a…

Python股票计算小程序(字符串格式化练习)

要求&#xff1a;打印的第一行使用f控制&#xff0c;第二行打印使用占位符&#xff0c;股价输出保留两位小数。 # 股价计算小程序 name"周氏集团" stock_price19.99 stock_code "9283" stock_price_daily_growth_factor1.2 growth_days7print(f"公司…

多线程场景下,使用QEventLoop使界面不卡顿的同时过滤用户输入

背景描述 有耗时的操作需要放到子线程中处理&#xff0c;但是此过程中不允许界面执行其他操作&#xff0c;不能使用阻塞界面的模态对话框实现。 可以通过QEventLoop在子线程执行的同时开启一个事件循环&#xff0c;使UI界面不卡顿&#xff0c;能够正常响应。但是这种不卡顿只是…

【Python进阶】继承进阶和私有权限

目录 一、继承进阶 1、方法重写 2、调用父类方法 3、多层继承 二、私有权限 1、私有属性 2、私有方法 面向对象基础&#xff1a;小白也能看懂的Python基础教程&#xff08;8&#xff09;-CSDN博客 一、继承进阶 1、方法重写 当父类的同名方法达不到子类的要求&#x…

阿里云上kubesphere安装配置 - 使用阿里云负载均衡

教程参考 https://www.kubesphere.io/zh/docs/v3.3/installing-on-linux/public-cloud/install-kubesphere-on-ali-ecs/ 环境配置 关闭防火墙关闭selinux关闭swap分区时间同步hosts解析内核参数设置检查DNS安装ipvs安装依赖组件安装、设置docker每台机器都需要操作 #安装工具…

crossJoin笛卡尔积

crossJoin笛卡尔积 在Spark中&#xff0c;crossJoin方法用于执行两个数据集之间的笛卡尔积操作。具体来说&#xff0c;如果有两个数据集&#xff08;DataFrame或Dataset&#xff09;&#xff0c;调用crossJoin方法将会生成一个新的数据集&#xff0c;其中包含两个原始数据集中所…

Monaco 中添加 CodeLens

CodeLens 会在指定代码行上添加一行可点击的文字&#xff0c;点击时可以触发定义的命令&#xff0c;效果如下&#xff1a; 通过调用 API 注册 LensProvider&#xff0c;点击时触发 Command&#xff0c;首先要注册命令&#xff0c;通过 editor.addCommand () 方法进行注册。三个…

7月9日学习打卡-回文链表,交叉链表

大家好呀&#xff0c;本博客目的在于记录暑假学习打卡&#xff0c;后续会整理成一个专栏&#xff0c;主要打算在暑假学习完数据结构&#xff0c;因此会发一些相关的数据结构实现的博客和一些刷的题&#xff0c;个人学习使用&#xff0c;也希望大家多多支持&#xff0c;有不足之…