【C++基础】初识C++(2)--引用、const、inline、nullptr

news2024/11/14 18:56:56

目录

一、引用

1.1  引用的概念和定义

1.2  引用的特性

1.3引用的使用

1.4  const引用

1.5  指针和引用的关系

二、inline

三、nullptr


一、引用

1.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;
}

 

注:C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的 <<和 >>,这⾥引⽤也和取地址使⽤了同⼀个符号&,⼤家注意使⽤⽅法⻆度区分就可以

1.2  引用的特性

  • 引⽤在定义时必须初始化
  • ⼀个变量可以有多个引⽤
  • 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
#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;
}

1.3引用的使用

  • 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率改变引⽤对象时同时改变被引⽤对象
  • 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
  • 引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的点,C++引⽤定义后不能改变指向,Java的引⽤可以改变指向。

 之前我们在另一个函数交换值需要传地址, 有了引用就可以直接传引用,此时rx就相当于x的别名, 而ry就相当于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语言中的指针,现用引用替代

#include<iostream>
using namespace std;
typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;
void STInit(ST& rs, int n = 4)
{
    rs.a = (STDataType*)malloc(n * sizeof(STDataType));
    rs.top = 0;

    rs.capacity = n;
} 
// 栈顶
void STPush(ST& rs, STDataType x)
{
// 满了, 扩容
    if (rs.top == rs.capacity)
    {
        printf("扩容\n");
        int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2;
        STDataType* tmp = (STDataType*)realloc(rs.a, newcapacity *sizeof(STDataType));

        if (tmp == NULL)
        {
        perror("realloc fail");
        return;
        } 
        rs.a = tmp;
        rs.capacity = newcapacity;
    } 
    rs.a[rs.top] = x;
    rs.top++;
} 

int& STTop(ST& rs)
{
    assert(rs.top > 0);
    return rs.a[rs.top];
} 
int main()
{
// 调⽤全局的
    ST st1;
    STInit(st1);
    STPush(st1, 1);
    STPush(st1, 2);
    cout << STTop(st1) << endl;

    STTop(st1) += 10;
    cout << STTop(st1) << endl;
    return 0;
}

还有这个是许多教材上喜欢写的,以前C语言的时候是不是很懵啊 ,⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针,但是很多同学没学过引⽤,导致⼀头雾⽔

#include<iostream>
using namespace std;
typedef struct SeqList
{
    int a[10];
    int size;
}SLT;

void SeqPushBack(SLT& sl, int x)
{}
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}LTNode, *PNode;
    // 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名
    // 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
    //void ListPushBack(LTNode** phead, int x)
    //void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PNode& phead, int x)
{
    PNode newnode = (PNode)malloc(sizeof(LTNode));
    newnode->val = x;
    newnode->next = NULL;
    if (phead == NULL)
    {
    phead = newnode;
    } 
    else
    {
    //...
    }
}
int main()
{
    PNode plist = NULL;
    ListPushBack(plist, 1);
    return 0;
}

1.4  const引用

• 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤
• 不需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场景下a*3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是时,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。
• 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象

举个栗子: 

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; //error
	const int& rb = a * 3;//right
	double d = 12.34;
	// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
	// int& rd = d; //error
	const int& rd = d; //right
	return 0;
}

权限放大只存在于指针和引用中,下面这两种不要搞混了 

 

1.5  指针和引用的关系

C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。
语法概念引⽤是⼀个变量的取别名不开空间指针是存储⼀个变量地址,要开空间
引⽤在定义时必须初始化指针建议初始化,但是语法上不是必须的
引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象
引⽤可以直接访问指向对象指针需要解引⽤才是访问指向对象
• sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)
• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

int main()
{
	int a = 0;
	int* p = &a;
	*p = 1;

	int& ra = a;
	ra = 2;

	return 0;
}

这串代码通过vs中的反汇编可以看出,指针和引用在底层是一样的,这里大家做一个了解就行了

二、inline

• ⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率。
• inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
• C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调试,C++设计了inline⽬的就是替代C的宏函数
inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。

#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;
}

 

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

三、nullptr

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

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

• 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;
}

本篇到这里就结束了,如有问题欢迎在评论区指正。感谢!下篇见!

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

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

相关文章

打造你的智能家居指挥中心:基于STM32的多协议(zigbee、http)网关(附代码示例)

1. 项目概述 随着物联网技术的蓬勃发展&#xff0c;智能家居正逐步融入人们的日常生活。然而&#xff0c;市面上琳琅满目的智能家居设备通常采用不同的通信协议&#xff0c;导致不同品牌设备之间难以实现互联互通。为了解决这一难题&#xff0c;本文设计了一种基于STM32的多协…

IOT物联可编程中控主机同时具备中控主机和交换机的功能

可编程中控主机同时具备中控主机和交换机的功能 GF-MAXCCP可编程中控主机确实同时具备中控主机和交换机的功能&#xff0c;这种设备在现代化会议室、音视频系统及其他集中控制场景中发挥着重要作用。以下是关于GF-MAXCCP可编程中控主机具备中控主机和交换机功能的详细解释&…

ubuntu基于cmakelist的Qt工程,如何将图片打包进二进制程序

qt界面使用的图片打包进入二进制可执行程序&#xff0c;可以避免发布的软件&#xff0c;因为路径问题无法加载图片的问题。 以下步骤参考自百度AI. 步骤如下&#xff1a; 1.创建一个新的Qt资源文件&#xff08;.qrc文件&#xff09; 2.在*.qrc文件中添加图片路径 qrc文件使用…

在Vue中,子组件向父组件传递数据

在Vue中&#xff0c;子组件向父组件传递数据通常通过两种方式实现&#xff1a;事件和回调函数。这两种方式允许子组件与其父组件进行通信&#xff0c;传递数据或触发特定的行为。 1. 通过事件传递数据 子组件可以通过触发自定义事件&#xff0c;并将数据作为事件的参数来向父组…

win11显示泛白关闭HDR

由于自己拿键盘胡乱玩了一会&#xff0c;然后发现显示泛白&#xff0c;很奇怪&#xff0c;用笔记本试了&#xff0c;不是显示器的问题&#xff0c;在网上找不到相关问题的回复&#xff0c;找显卡客服怎么都不好使&#xff0c;卸载显卡驱动可行&#xff0c;但是装上又有问题了。…

案例 | 人大金仓助力山西政务服务核心业务系统实现全栈国产化升级改造

近日&#xff0c;人大金仓支撑山西涉企政策服务平台、政务服务热线联动平台、政务网、办件中心等近30个政务核心系统完成全栈国产化升级改造&#xff0c;推进全省通办、跨省通办、综合业务受理、智能审批、一件事一次办等业务的数字化办结进程&#xff0c;为我国数字政务服务提…

云盘挂载 开机自动模拟 cmd- alist server

云盘挂载 开机自动模拟 cmd- alist server 打开Kimi智能助手, 网址:Kimi.ai - 帮你看更大的世界 (moonshot.cn) 问他: 帮我写一个vbs命令:在D:\sky目录下, 然后cmd, 进入命令行后, 输入 alist server 然后回车 这里 这个目录, 换成自己的 alist.exe所在目录 下面是我完善的示…

对照ui图进行大屏幕适配,echerts适配

1.先找到ui图&#xff0c;我这边是1920*1080的屏幕进行的设计 2.在界面找到跟样式的字体大小&#xff0c;进行设置&#xff0c;一般ui设置字体大小便可 3.在js中写入原生js代码 function adapter() {//获取布局视口宽度&#xff0c;布局视口设备横向独立像素值const dpWidth…

锂电池剩余寿命预测 | Matlab基于Transformer的锂电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于Transformer的锂电池剩余寿命预测 Matlab基于Transformer的锂电池剩余寿命预测&#xff08;单变量&#xff09; 运行环境Matlab2023b及以上 NASA数据集&#xff0c;B0005号电池数据训练&#xff0c;B00…

SpringCloud集成kafka集群

目录 1.引入kafka依赖 2.在yml文件配置配置kafka连接 3.注入KafkaTemplate模版 4.创建kafka消息监听和消费端 5.搭建kafka集群 5.1 下载 kafka Apache KafkaApache Kafka: A Distributed Streaming Platform.https://kafka.apache.org/downloads.html 5.2 在config目录下做…

OpenGL笔记五之VBO与VAO

OpenGL笔记五之VBO与VAO 总结自bilibili赵新政老师的教程 code review! 文章目录 OpenGL笔记五之VBO与VAO1.VBO2.VAO3.VBO与VAO对比 1.VBO 代码 void prepareVBO() {//1 创建一个vbo *******还没有真正分配显存*********GLuint vbo 0;GL_CALL(glGenBuffers(1, &vbo))…

使用UDP通信接收与发送Mavlink2.0协议心跳包完整示例

1.克隆mavlink源码 https://github.com/mavlink/mavlink.git 2.进入mavlink目录,安装依赖 python3 -m pip install -r pymavlink/requirements.txt 3.生成Mavlink的C头文件 mavlink % python3 -m pymavlink.tools.mavgen --lang=C --wire-protocol=2.0 --output=generated…

【大模型】2024大模型典型示范应用案例集——附219页PDF

2024 年是大模型深入赋能千行百业&#xff0c;融入实体经济&#xff0c;助力科技创新的一年。截至今年5 月&#xff0c;我国国产大模型的数量已经超过300 个&#xff0c;预示着大模型在各行业场景的创新应用和深度拓展&#xff0c;对培育新质生产力、高水平赋能新型工业化、推动…

LINUX系统编程:基于环形队列和信号量的生产者消费者模型

目录 1.环形队列 2.加上信号量的理解 3.代码 1.环形队列 环形队列使用vector封装出来的。 环形队列可以实现并发生产和消费&#xff0c;就是在消费的同时也可以生产。 这个是建立在生产者消费者位置不重合的情况下。 因为位置重合之后&#xff0c;环形队列为空或者满&#xf…

Tomcat组件概念和请求流程

Tomcat:是一个Servlet容器(实现了Container接口)&#xff0c;容器分层架构从上到下分为。Engine(List<Host>)->Host(List<Context>)->Context(List<Wrapper>)->Wrapper(List<Servlet>); Engine:引擎&#xff0c;Servlet 的顶层容器&#xff0…

使用uni-app和Golang开发影音类小程序

在数字化时代&#xff0c;影音内容已成为人们日常生活中不可或缺的一部分。个人开发者如何快速构建一个功能丰富、性能优越的影音类小程序&#xff1f;本文将介绍如何使用uni-app前端框架和Golang后端语言来实现这一目标。 项目概述 本项目旨在开发一个个人影音类小程序&#…

dm-verity hashtree的结构

参考了&#xff1a;实现 dm-verity | Android Open Source Project (google.cn)。基于这个添加了一层原始数据&#xff0c;便于理解。 结构图如下&#xff1a; 对hashtree结构图的解释&#xff1a; dev data&#xff1a;表示我们的分区数据。这里我们将dev data按照指定的大…

计网(1.1~1.4)

1.1计算机网络在信息时代的作用 21世纪的重要特征数字化、网络化和信息化 有三类网络&#xff1a;电信网络、有线电视网络和计算机网络 互联网两个重要基本特点&#xff0c;即连通性和共享 1.2因特网概述 &#xff08;1&#xff09;网络、互联网和互连网 网络:由若干结点和连接…

安装jenkins最新版本初始化配置及使用JDK1.8构建项目详细讲解

导读 1.安装1.1.相关网址1.2.准备环境1.3.下载安装 2. 配置jenkins2.1.安装插件2.2.配置全局工具2.3.系统配置 3. 使用3.1.配置job3.2.构建 提示&#xff1a;如果只想看如何使用jdk1.8构建项目&#xff0c;直接看3.1即可。 1.安装 1.1.相关网址 Jenkins官网&#xff1a;https…

LabVIEW前面板占满整个屏幕(转)

希望在运行一个LabVIEW程序时&#xff0c;它的前面板能够占据整个屏幕&#xff0c;且不显示Windows的任务栏或其他任何的LabVIEW菜单选项。怎样才能实现这一功能&#xff1f; 您可以通过手动配置或编程的方式实现该功能。 手动配置VI属性 您可以通过以下操作&#xff0c;将…