【C++笔记】引用和const引用以及inline和nullptr

news2024/11/28 4:33:36

【C++笔记】引用和const引用以及inline和nullptr

在这里插入图片描述

🔥个人主页大白的编程日记

🔥专栏C++笔记


文章目录

  • 【C++笔记】引用和const引用以及inline和nullptr
    • 前言
    • 一.引用
      • 1.1引用的概念和定义
      • 1.2引用的特性
      • 1.3引用的使用
    • 二. const引用
    • 三.指针和引用的关系
    • 四.inline
    • 五.nullptr
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了命名空间。这期我们来讲引用。话不多说,咱们进入正题!向大厂冲锋!

一.引用

1.1引用的概念和定义

引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间, 它和它引用的变量共用同⼀块内存空间。比如:水壶传中李逵,宋江叫"铁牛",江湖上⼈称"黑旋风";林冲,外号豹子头;

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

在这里插入图片描述

  • 引用的使用
    类型& 引用别名=引用对象;
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;
}




注意引用并没有开空间。引用只是给对象取别名。所以d++,a,b,c也都++。他们的地址也是一样的。

1.2引用的特性

  • 初始化
    引用在定义时必须初始化。
  • 引用个数
    ⼀个变量可以有多个引用。这里我们用b给a取别名。再给用c给b取别名。其实也是给a取别名。
int main()
{
	int a = 0;
	// 引⽤:b和c是a的别名
	int& b=a;
	int& c = a;
	// 也可以给别名b取别名,d相当于还是a的别名
	int& d = b;
}
  • 唯一对象
    引用⼀旦引用⼀个实体,再不能引用其他实体。
int main()
{
	int a = 10;
	// 编译报错:“ra” :必须初始化引⽤
	//int& ra;
	int& b = a;
	int c = 20;
	b = c;
	// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,
	// 这⾥是⼀个赋值
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	return 0;
}

1.3引用的使用

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

  • 改变对象
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	Swap(&a, &b);
	return 0;
}

以前我们交换变量只能传地址。因为形参是拷贝,形参的改变不影响实参。

void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

现在我们就可以这样写。因为引用就是引用对象本身。只是取别名而已。
引⽤传参跟指针传参功能是类似的,也不用&和*,引用传参相对更方便⼀些。

  • 引用传参
int Add(int& x, int& y)
{
	int ret=x + y;
	return ret;//拷贝生成临时对象,再返回临时对象
}

这是传值返回。他拷贝生成一份临时对象,再将临时对象返回。

int& Add(int& x, int& y)
{
	int ret=x + y;
	return ret;//传引用返回,直接返回对象的别名
}

这是传引用返回,直接返回ret的别名。这样就能减少传值返回的拷贝。提高效率。
但是这样写会产生野引用的问题(类似野指针)。因为ret是局部变量。函数结束后就会销毁。这时我们再通过别名去访问ret就会产生野引用。

  • 引用和指针
    引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引用(如Java)是有很⼤的区别的,除了用法,最大的点,C++引用定义后不能改变指向,Java的引用可以改变指向。

  • 教材使用
    ⼀些主要用C代码实现版本数据结构教材中,使⽤C++引用替代指针传参,目的是简化程序,避开复杂的指针,但是很多同学没学过引用,导致⼀头雾水。

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

二. const引用

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

权限变化有三种情况:权限放大 权限平移 权限缩小。
在这里插入图片描述

int main()
{
	const int a = 0;
	const int& b = a;//权限平移
}


a只读不写,b也只读不写。

int main()
{
	const int a = 0;
	int& b = a;//权限放大
}


a只读只写,b取别名后却能修改a,这是不合理的。

int main()
{
	int a = 0;
	const int& b = a;//权限缩小
}

a可读可写,b取别名后只读取a不修改,这是合理的。

int main()
{
	const int a = 10;
	int& b = a;//b取别名可读可写 权限放大
	int c = b;//b拷贝给c c可读可写并不是a可读可写。
}

但是大家注意这段代码c是否涉及权限放大呢?

没有因为这只是将a拷贝给c。c可读可写并没有影响a。a依然还是可读不可写。

  • 临时对象
    所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。
    一般临时对象出现在返回值,表达式的结果,类型转化等场景。
    需要注意的是临时对象具有常性。不能修改。
int main()
{
	int a = 10;
	const int& ra = 30;
	// 编译报错: “初始化” :⽆法从“int”转换为“int& ”
	int& rb = a * 3;
	double d = 12.34;
	// 编译报错:“初始化” :⽆法从“double”转换为“int& ”
	int& rd = d;
	return 0;
}


这段代码为什么报错呢?

因为他们都生成了临时对象。临时对象具有常性。可读不可写。
所以要使用const引用。

int main()
{

	int a = 10;
	const int& ra = 30;
	const int& rb = a * 3;
	double d = 12.34;
	const int& rd = d;
	return 0;
}


那临时对象销毁后。这时的引用不就是野引用了吗。

不会,引用临时对象后。临时对象的生命周期和引用一样。

  • 使用场景
    那const引用有什么用呢?
void f(int x)
{

}

例如我们写一个函数f。这样写就会增加拷贝。所以我们会用引用做参数。

void f(int& x)
{

}
int main()
{ 
	int a = 2;
	int b = 1;
	double d = 1.5;
	f(3);//常量不可改变
	f(a + b);//临时对象 常性
	f(1.5); // 临时对象 常性
	return 0;
}


但是如果我们不修改x的话。这样写常量,表达式来传参都传不过去。因为他们具有常性。

但是如果我们把const加上就可以传参。这样我们的传参范围更宽泛。这就是const引用的价值。

三.指针和引用的关系

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

  • 语法概念上引用是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
  • 引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
  • 引用在初始化时引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。
  • 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
  • sizeof中含义不同,引用结果为引⽤类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)
  • 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全⼀些。

但是底层上指针和引用都没有区别。转到汇编指令都是一样的。没有引用的概念。

四.inline

  • 内联的概念
    用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,因为建立栈帧还是有消耗的。这样就可以提高效率。
    所以inline其实是用来提效的。

其实就相当于在调用的地方替换在函数内部展开。相当于宏的作用。

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

可是我们Add不是内联函数了吗。怎么还有call。还是展开了?

  • Debug展开
    vs编译器debug版本下⾯默认是不展开inline的,这样方便调试。
    debug版本想展开需要设置⼀下以下两个地方。 在这里插入图片描述

  • 编译器决定
    inline对于编译器而言只是⼀个建议,也就是说,你加了inline编译器也可以选择在调用的地方不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适用于频繁调用的短小函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。

也就是内联对编译器只是建议。如果建议合理他就听,不合理就不听。因为无条件展开也是会付出代价的。

不合理的展开,如大段代码的函数展开就会导致可执行程序变大。

  • 宏和内联
    C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不方便调试,C++设计了inline目的就是替代C的宏函数。

    所以为了解决宏的问题。同时方便调试。设计出了内联。
  • 声明和定义
    inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。

    所以内联函数声明和定义都放在.h文件即可。

五.nullptr

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

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

C++中NULL被定义为字面量0,0编译器默认时int类型。
C语言中NULL被定义为void*指针类型。
所以在某些场景下会出现歧义。

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(intx),因此与程序的初衷相悖。
	return 0;
}


这里f(NULL)本想调用第二个函数。却调用到第一个函数。因为C++NULL定义为字面两0,默认属于int整型。
那是不是把NULL改为void就可以了呢?

也不行因为C++中void
不能转为int*。这样两个类型都不匹配。
但是C语言检查不严格。void*可以转为任意类型的指针。
在这里插入图片描述
在这里插入图片描述

为了解决这个问题C++涉及了nullptr.

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

后言

这就是C++的引用和一些语法。大家多加学习掌握。今天就分享到这,感谢大家的耐心垂阅!咱们下期见!拜拜~

在这里插入图片描述

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

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

相关文章

自动微分autograd实践要点

目录 定义Value手动定义每个 operator 的 _backward() 函数构建反向传播计算链 本文主要参考 反向传播和神经网络训练 大神Andrej Karpathy 的“神经网络从Zero到Hero 系列”之一&#xff0c;提炼一些精要&#xff0c;将反向传播的细节和要点展现出来 定义Value 第一步首先要…

基于BlockQueue的生产消费模型及Linux中的信号量

基于BlockQueue的生产消费模型 Task.hpp #pragma once#include<cstdio> #include<iostream> #include<string> #include<functional>using namespace std; class CalTask {using func_tfunction<int(int,int,char)>;//typedef function<int(…

OW-VISCap——开放世界视频实例分割方法研究

概述 论文地址&#xff1a;https://arxiv.org/pdf/2404.03657 本文提出了一种名为 OW-VISCap&#xff08;开放世界视频实例分割和字幕&#xff09;的方法。其三大贡献是 开放世界对象查询&#xff1a;除了已知对象查询外&#xff0c;还引入了开放世界对象查询&#xff0c;以发…

python爬虫521

爬虫521 记录 记录 最近想学爬虫&#xff0c;尝试爬取自己账号下的文章标题做个词云 csdn有反爬机制 原理我就不说啦 大家都写了 看到大家结果是加cookie 但是我加了还是521报错 尝试再加了referer 就成功了(╹▽╹) import matplotlib import requests from wordcloud impor…

第2章-03-HTTP协议,POST与GET等请求方式

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年CSDN全站百大博主。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于专栏:Web爬虫入门与实战精讲,后续完整更新内容如下。 文章…

揭秘移动IP:为何定位精度多停留在城市级?

随着电子信息技术的日新月异&#xff0c;移动网络已深度融入我们的日常生活&#xff0c;从2G的初步尝试到5G的飞跃&#xff0c;不仅数据传输速度实现了质的飞跃&#xff0c;更催生了丰富多样的移动应用场景与功能。在这一变革浪潮中&#xff0c;移动IP&#xff08;Mobile IP&am…

再见百度网盘,我有ZFile了!!【送源码】

项目简介 ZFile是一款强大的在线网盘管理系统&#xff0c;专为个人用户设计&#xff0c;能够将不同类型的存储资源统一在一个简洁易用的界面中进行管理和访问。通过ZFile&#xff0c;用户不再需要记住并登录多个云存储平台&#xff0c;所有的文件管理操作都可以在一个地方完成&…

Tomcat:Web 领域的闪耀明珠,魅力何在?

一、Web技术 HTTP 协议&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的一种网络协议。它的主要作用是在客户端和服务器之间传输超文本数据&#xff0c;如网页、图片、视频等。 HTTP 协议的特点 无状态性 HTTP 协议是…

【数学建模】优化模型——两辆平板车装货问题

问题描述 包装箱规格&#xff1a;共有七种规格的包装箱&#xff0c;每种包装箱的厚度&#xff08;t&#xff09;和重量&#xff08;w&#xff09;不同。表中列出了每种包装箱的厚度、重量及数量。 平板车限制&#xff1a; 每辆平板车的可用装载长度为10.2米&#xff08;1020厘…

《QT从基础到进阶·七十三》Qt+C++开发一个python编译器,能够编写,运行python程序

1、概述 源码放在文章末尾 该项目利用QtC实现了一个简易的python编译器&#xff0c;类似pycharm或vsCode这样的编译器&#xff0c;该python编译器支持如下功能&#xff1a; &#xff08;1&#xff09;支持编写python程序 &#xff08;2&#xff09;编写代码时有代码补全提示 &…

写给大数据开发:如何优化临时数据查询流程

你是否曾因为频繁的临时数据查询请求而感到烦恼&#xff1f;这些看似简单的任务是否正在蚕食你的宝贵时间&#xff0c;影响你的主要工作&#xff1f;如果是&#xff0c;那么这篇文章正是为你而写。 目录 引言&#xff1a;数据开发者的困境问题剖析&#xff1a;临时数据查询的…

MKS电源GMW-25RF Plasma Generator手侧

MKS电源GMW-25RF Plasma Generator手侧

C语言基础(八)

1、标准库函数&#xff1a; 测试代码1&#xff1a; #include <stdio.h> // 标准库函数头文件导入 // 自定义函数 int add(int a, int b) { return a b; } // 声明回调函数类型 typedef void (*Callback)(int); // 调用回调函数的函数 void process(Callb…

网络编程第三天

服务器&#xff1a; #include<sys/types.h> // 支持套接字地址结构 #include <sys/socket.h> // 提供套接字API #include <netinet/in.h> // 定义IP地址结构体 #include <string.h> // 提供字符串操作函数 #include <stdio.h> // 提供标准I/O操…

宠物空气净化器哪款能吸毛?希喂、米家宠物空气净化器测评分享

养猫最令人困扰的&#xff0c;就是掉毛与难以彻底消除的异味&#xff0c;这两个问题就成了养猫生活中的一大挑战。每当换季或是猫咪自我梳理时&#xff0c;家中便被一层细腻的绒毛覆盖&#xff0c;从地板到沙发&#xff0c;从床单到衣物&#xff0c;甚至是空气中都漂浮着细小的…

Dockerfile中CMD和ENTRYPOINT区别以及结合使用

1. 简述 在Dockerfile中,CMD和ENTRYPOINT都是用于指定容器启动时要运行的命令,但它们在使用方式和目的上有所不同。 下面将分别解释这两个指令的含义,并通过示例说明它们之间的区别,以及常见的结合使用案例。 Dockerfile制作镜像流程图: 2. CMD CMD指令用于指定容器启…

RocketMQ环境搭建(宝塔)

文章目录 1.介绍2.RocketMQ介绍1.官网2.基础概念3.组件架构 3.安装1.安装宝塔面板1.更新系统2.安装宝塔面板3.开放33142端口 2.创建存放软件的文件夹3.将软件上传到soft目录下4.安装unzip5.进入目录解压软件6.进入bin目录7.修改两个sh文件配置内存小一点8.在/usr/local/soft/ro…

Maven-08.依赖管理-生命周期

一.生命周期 Maven中的生命周期就是描述一次maven项目构建要经历那些阶段。包含clean&#xff0c;default和site三个。这三个生命周期时相互独立的。所谓相互独立&#xff0c;就是每套生命周期中的阶段互不干扰。 阶段是生命周期中最细化的操作。我们重点关注5个阶段&#xf…

教你学习企业高性能web服务器-nginx

一、web服务介绍 1、Apache的三种模型 &#xff08;1&#xff09;Apache prefork 预派生模式&#xff0c;有一个主控制进程&#xff0c;然后生成多个子进程&#xff0c;使用select模型&#xff0c;最大并发1024每个子进程有一个独立的线程响应用户请求相对比较占用内存&…

笔记(day24)正则表达式

一、正则表达式 1.1 概述 正则表达式定义了字符串的匹配模式,可以用来进行搜索,编辑,或处理文本 并不仅限于某一种语言,但是在每种语言中有细微的差别 1 数据校验、格式校验 2 数据提取 1.2 语法 元字符描述\将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如&a…