【C++】引用、内联函数、auto关键字等

前言:在前面我们讲解了C++入门基础的一些学习例如命名空间、缺省参数、函数重载等。今天我们将进一步的学习,跟着博主的脚步再次往前迈一步吧。

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:高质量C++学习 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


  • 目录标题

    • C++入门
      • 引用
        • 引用的特性与引用的使用
          • 引用的使用
          • 变量与引用的变量共用同一块内存空间
          • 引用的特性
        • 常引用
        • 引用的使用场景
          • 做参数
          • 做返回值
      • 内联函数
        • 内联函数的特性
        • 内联函数的限制和注意事项
          • 基于范围的for循环(C++11)
        • 指针空值nullptr(C++11)

C++入门

引用

C++中引用是一种特殊的变量类型,它类似于一个别名,可以用来引用另一个变量。引用用&符号来声明,对于一个已经存在的变量,可以使用引用来访问该变量的值。如果我们还是无法理解什么是引用,我们可以通俗的理解成,你的名字叫张三,但是你的小名叫三三、阿三等等,无论小名是什么,这个名字的本质都是指代着你这个人。
语法说明:类型& 引用变量名(对象名) = 引用实体

引用可以有以下特点

  1. 引用必须在声明时初始化,并且不能改变引用的目标。一旦指定了引用的目标,它将始终引用该目标。

  2. 引用可以用来修改引用的目标。通过引用可以修改引用所指向的变量的值。

  3. 引用不能引用空值或不存在的对象。

  4. 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

  5. 不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。


引用的特性与引用的使用

引用的使用
int main() {
    int x = 10;
    int& ref = x;//对x进行引用

    cout << "x = " << x << endl; // 输出 x = 10
    cout << "ref = " << ref << endl; // 输出 ref = 10

    ref = 20; // 修改引用的目标

    cout << "x = " << x << endl; // 输出 x = 20
    cout << "ref = " << ref << endl; // 输出 ref = 20

    return 0;
}

在这里插入图片描述


变量与引用的变量共用同一块内存空间
void TestRef()
{
    int a = 10;
    int& ra = a;//<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

int main()
{
    TestRef();
    return 0;
}

在这里插入图片描述
此时我们也不难发现,a和ra所在的空间上的地址是相同的,因此可以知道,a和ra共用同一块内存空间。
在这里插入图片描述


引用的特性

如果要博主解释引用的话,博主目前短暂的理解是,引用如指针是十分相向的,但是引用一旦指向一个实体就不能改变其指向了。

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

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

  3. 引用一旦引用一个实体,再不能引用其他实体

int main()
{
	int a = 10;
	int& ra = a;
	//int& ra;此时会报错,因为引用在定义的时候必须初始化
	int& rra = ra;//一个变量可以拥有多个引用
	cout << "原本的值" << endl;
	cout << "a = " << a << " ra = " << ra << " rra = " << rra << endl;//引用的变量与变量本身共用同一块空间
	cout <<"地址: " << "a = " << &a << " ra = " << &ra << " rra = " << &rra << endl;

	cout << "修改后的值" << endl;
	a++;//对引用的修改是会影响变量本身的
	ra++;
	rra++;
	cout << "a = " << a << " ra = " << ra << " rra = " << rra << endl;
	cout << "地址: " << "a = " << &a << " ra = " << &ra << " rra = " << &rra << endl;
	return 0;
}

在这里插入图片描述


常引用

const这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。和const修饰变量一样,被const修饰的引用只能够读,而不能够写,即不能修改引用变量的值。接下来我们将通过几个例题来帮助我们理解,如下:

例1:请问如下两个语句,哪一个会报错,哪一个不会。

 const int a = 10;//语句1
 int& ra = a;  //语句2

代码解读:

在如上的两个代码中,语句1将10这个常量传给了整型a,并且a无法被修改,因此语句1正确,而语句二,我们将一个无法被修改的a值传给引用ra,此时我们不难发现,如果ra可以这样引用的话不就代表a值是可以修改的了嘛?那这个const修饰的a不就没有任何意义了嘛?因此不难判断出语句2是错误的。因此正确的写法是:const int& ra = a; 此时我们用const修饰的引用变量ra就可以达到该引用值无法被修改。


例题2:请问如下两个语句,哪一个会报错,哪一个不会。

int& b = 10; //语句1
const int& b = 10;//语句2

代码解读:

我们可以知道10是一个常量对吧,10能被修改嘛?当然不可以,因此我们怎么可能可以通过引用变量直接来引用10呢?这不开玩笑嘛,如果b能够直接修改这个10,那10是什么表示呢?如果不懂的可以看下图。那么语句2呢?我们用const修饰引用变量,那么此时被引用的变量就无法修改,因此语句2正确。

在这里插入图片描述


例题3:请问如下两个语句,哪一个会报错,哪一个不会。

 double d = 12.34;
 int& rd = d; //语句1
 const int& rd = d;//语句2

代码解读:

语句1直接对d进行引用的同时,在编译器中会对d进行强转成一个整型12,而12具有常性因此如果我们直接对其进行引用的过程中会导致我们墙面讲到的情况,如果对一个常量直接引用会导致数据发生修改,因此我们需要加上const对其进行修饰,如果不能理解可以看下图。

在这里插入图片描述
整体代码测试:

void TestConstRef()
{
    const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量
    const int& ra = a;
    // int& b = 10; // 该语句编译时会出错,b为常量
    const int& b = 10;
    double d = 12.34;
    //int& rd = d; // 该语句编译时会出错,类型不同
    const int& rd = d;
    cout << "a = " << a << " ra = " << ra << " b =  " << b << " d = " << d << " rd = " << rd << endl;
}

int main()
{
    TestConstRef();
	return 0;
}

运行结果
在这里插入图片描述


引用的使用场景

做参数

在学习C语言的过程中我们知道,如果将一个数传递给函数,形参是实参的一份临时拷贝,此时空间占用是十分大的,而引用本质就是直接把该数本身传递过去,直接对这个数可以进行修改省区了对该数进行拷贝的过程减少了空间的消耗.
实例演示

void Swap(int& a, int& b)//此时我们直接用引用结接收
{
	int tmp = 0;
	tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int n = 3;
	int m = 4;
	cout <<"修改前: " << n << ' ' << m << endl;
	Swap(n, m);
	cout <<"修改后: " << n << ' ' << m << endl;
	return 0;
}

在这里插入图片描述

具体等作为参数的情况,我们在后面的学习会持续体会到的,到时候感受肯定是会与现在不太一样,因此我们现在就对其做一个初步的使用即可.


做返回值

我们接下来依然通过几个例子来帮助我们理解作为返回值的情况.
例1:

int& Func()
{
	static int n = 0;//static修饰的局部变量出了作用域不会结束
	n++;
	printf("&n:%p\n", &n);
	return n;
}

int main()
{
	int ret = Func();
	cout <<"n = "<< ret << endl;//查看此时n的地址与值
	int& rret = Func();
	printf("&rret:%p\n", &rret);
	return 0;
}

此时我们不难发现将引用作为返回值返回给了函数
在这里插入图片描述


例题2:

int& Add(int a, int b)
{
    static int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :" << ret << endl;
    int& m = Add(3, 4);
    cout << "Add(3, 4) is :" << m << endl;
    return 0;
}

代码分析:
这里我们将1,2先传过去因此一开始返回的是3没有任何疑问,在第二次传递给3和4的时候我们需要注意达到是static修饰的局部变量出了作用域并不会结束,static修饰局部变量会进行一次初始化,并且只会进行一次,因此我们传过去的3和4并没有相加,返回的依然是上一次的结果3.
在这里插入图片描述


例题3:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :" << ret << endl;
    Add(3, 4);
    cout << "Add(1, 2) is :" << ret << endl;
    return 0;
}

代码分析:
在函数 Add 中,局部变量 c 的生命周期仅限于函数内部。
当函数执行完毕后,c 被销毁,而返回的引用 ret 将指向一个不存在的对象。当再次使用这个引用时,就会出现未定义的行为。因为此时我们又传递了3和4此时我们并没有返回值接收,然后再次调用ret的时候所引用的就是3和4那块函数所指向空间的值.
在这里插入图片描述
总结一下:出了函数作用域返回变量不存在了,不能用引用返回因为此时指向的空间已经被销毁了可以理解成为一个野引用,因此出了函数作用域返向变量存在,才能用引用返回.

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
    一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
    位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

内联函数

C++内联函数是一种特殊的函数,其函数体在调用点处直接展开,而不是按照函数调用的方式进行执行。这样可以避免函数调用的开销,提高程序的执行效率。
在C++中,可以通过将函数定义放在函数声明之前,并在函数声明处加上关键字“inline”来定义内联函数

内联函数的特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
    用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
    行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

内联函数的限制和注意事项

  1. 内联函数的定义必须在每个使用该函数的文件中可见。
  2. 内联函数一般放在头文件中,以便在多个源文件中使用。
  3. 内联函数不应包含复杂的逻辑或大量的代码,以避免代码膨胀。
  4. 对于递归函数、带有循环或开销较大的函数,不建议使用内联函数。

实例演示:

inline int add(int a, int b) //内联函数的定义
{
	return a + b;
}

int main()
{
	cout << add(19, 21) << endl;
	return 0;
}

在这里插入图片描述


auto 关键字在C语言中就已经存在了,只不过在C语言中它的作用是声明自动变量:auto int a = 0;
在C++中,关键字auto用于自动推导变量的类型。它可以让编译器根据变量初始化表达式的类型自动推导出该变量的类型,从而简化变量类型的声明和初始化过程

使用auto关键字有以下几点注意事项:

  1. auto关键字只能在变量的声明和初始化过程中使用,不能用于函数的返回类型、函数参数类型、类成员的类型等。
  2. auto关键字通常用于推导变量的类型,不适用于推导引用或指针的类型。如果需要推导引用或指针的类型,可以使用auto&或auto*。
  3. 推导出的变量类型与表达式的类型是一致的,包括const、引用、指针等限定符。
  4. auto关键字在编译时自动推导变量类型,而不是在运行时进行类型推导。
    实例演示:
void func(int a, int b)
{
	//.....
	//.....
}

int main()
{
	int i = 0;
	int j = i;
	auto k = i;//auto可以通过右边的类型自动推导左边的类型
	auto p1 = &i;
	void(*pf1)(int, int) = func;//函数指针
	auto pf1 = func;//用auto直接直接识别
}
基于范围的for循环(C++11)

在C++98中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
     array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
     cout << *p << endl;
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。ps:目前我们只讲怎么用即可,以后会给大家详细讲解
实例演示:

int main()
{
	for (auto e : array)//依次取数组中的值赋值给e,自动迭代,自动判断结束
	{
		cout << e << " ";
	}
}	

在这里插入图片描述

int main()
{
	for (auto& e : array)//e是对数组里面的值的拷贝,所以无法直接赋值,得引用
	{
		e *= 2;
	}
	for (auto e : array)
	{
		cout << e << " ";
	}
}

在这里插入图片描述


指针空值nullptr(C++11)

在这里我们只需要知道nullptr是替代原本的NULL即可

在C++中,nullptr是一种特殊的空指针常量。它被用来表示一个空指针,即指向没有任何对象的指针。
在早期的C++版本中,常常使用NULL来表示空指针。然而,NULL实际上是一个宏定义,一般被定义为整数0。这样的定义使得在一些情况下,NULL可能会被误用。
为了解决这个问题,C++11引入了nullptr。nullptr是一个关键字,用来表示空指针,不会被隐式地转换成其他类型。使用nullptr可以提高代码的可读性和安全性.总而言之,nullptr是C++11中用来表示空指针的关键字,相对于早期的NULL常量,nullptr更加安全和可读性更好.


好啦,今天的内容就到这里啦,下期内容预告类和对象


结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🌏🗺️ 这里祝各位寒假愉快 💞💞

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 源码解析 - 持续更新

开始 spring initilizer&#xff1a;根据依赖构建工具、springboot 版本等生成 Java 工程。手把手教你手写一个最简单的 Spring Boot Starter Starter 命名规则 Spring 官方定义的 Starter 通常命名遵循的格式为 spring-boot-starter-{name}&#xff0c;例如 spring-boot-star…

目标检测数据集制作(VOC2007格式数据集制作和处理教程)

VOC2007数据集结构&#xff08;目标检测图像分割&#xff09; #VOC2007数据集结构如下&#xff1a; VOC2007|-Annotations#里面存放的是每一张图片对应的标注结果&#xff0c;为XML文件&#xff0c;#标注完成后JPEGImages每张图片在此都有一一对应的xml文件|-ImageSets#存放的是…

uniapp v3组合式语法-项目创建

打开uni编译器HbuildX,右键新建项目,出现如下界面 输入完项目名称和地址后.选择红色箭头所指处的vue版本3 然后点击确认,即可创建uniapp,v3项目 创建完项目后,可以看到如下项目结构 项目结构uniapp官网上已经写的很详细了,这里就使用uniapp官网的介绍 一个uni-app工程&…

用BufferedReader快速输入有巨坑!!大佬们都是这么输入的

刷洛谷题单第二天&#xff0c;今天第一道题还是挺简单的&#xff0c;但是又被现实给了一级大逼兜子&#xff0c;做这题关键还是想熟悉Java的快速输入。还有做这道题很容易出现RE错误&#xff0c;下面总结了常见的RE错误导致情况&#xff08;点击跳转下文&#xff09; 我的上篇…

增存量市场大爆发!国产通信中间件「反攻」

梳理2023年智能驾驶的发展脉络可见&#xff0c;消费者对智能驾驶的认可度和接受度越来越高&#xff0c;带动高速NOA迈向了规模化普及新阶段&#xff0c;城市NOA初露锋芒。 从更长远的行业变革周期来看&#xff0c;智能驾驶的技术迭代还在继续&#xff0c;叠加电子电气架构的深…

软件测试人员常用的功能测试方法分享

功能测试就是对产品的各功能进行验证&#xff0c;根据功能测试用例&#xff0c;逐项测试&#xff0c;检查产品是否达到用户要求的功能。 常用的测试方法如下&#xff1a; 1. 页面链接检查 每一个链接是否都有对应的页面&#xff0c;并且页面之间切换正确。 2. 相关性检查 删除/…

基于springboot的房屋交易系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

工业自动化步进电机用电机驱动芯片TMC5160

TMC5160 数据手册 TMC5160 集成了完整的运动控制功能、高质量的电流调节功能和强大的外部 MOSFET 驱动&#xff0c;涵盖了多种从电池供电的高效系统到 20 安培电机线圈电流的嵌入式应用。TMC5160 包含驱动电机所需的全部智能。配置 TMC5160 目 标 位 置 就 可 以 驱 动 电 机 。…

C++初阶:C/C++内存管理、new与delete详解

之前结束了类与对象&#xff1a;今天进行下面部分内容的学习 文章目录 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.C动态内存管理方式3.1new/delete操作内置类型3.2new和delete操作自定义类型 4.operator new与operator delete函数5.new和…

题目:冒险者公会(蓝桥OJ 3611)

问题描述&#xff1a; 解题思路&#xff1a; 官方&#xff1a; 注意点&#xff1a; 在前期的排序操作&#xff0c;因为需要找到如样例所示的每轮最大&#xff0c;因此我们需要用0代替没有的委托&#xff08;即样例斜杠&#xff09;。如何用0代替&#xff1a;将村庄委托数量默认…

基于springboot校园交友网站源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

ArcgisForJs快速入门

文章目录 0.引言1.前端代码编辑工具2.使用ArcgisForJs创建一个简单应用3.切片地图服务图层4.动态地图服务图层5.地图事件 0.引言 ArcGIS API for JavaScript是一款由Esri公司开发的用于创建WebGIS应用的JavaScript库。它允许开发者通过调用ArcGIS Server的REST API&#xff0c…

C++笔记之RTTI、RAII、MVC、MVVM、SOLID在C++中的表现

C++笔记之RTTI、RAII、MVC、MVVM、SOLID在C++中的表现 —— 杭州 2024-01-28 code review! 文章目录 C++笔记之RTTI、RAII、MVC、MVVM、SOLID在C++中的表现1.RTTI、RAII、MVC、MVVM、SOLID简述2.RAII (Resource Acquisition Is Initialization)3.RTTI (Run-Time Type Informat…

ABeam Insight | 大语言模型系列 (1) : 大语言模型概览

大语言模型系列 引入篇 ABeam Insight 自从图灵测试在20世纪50年代提出以来&#xff0c;人类一直不断探索机器如何掌握语言智能。语言本质上是一个由语法规则支配的错综复杂的人类表达系统。 近年来&#xff0c;具备与人对话互动、回答问题、协助创作等能力的ChatGPT等大语…

江科大stm32学习笔记6——GPIO输入准备

一、按键消抖 由于按键内部使用的是机械式弹簧片&#xff0c;所以在按下和松开时会产生5~10ms的抖动&#xff0c;需要通过代码来进行消抖。 二、滤波电容 在电路中&#xff0c;如果见到一端接在电路中&#xff0c;一端接地的电容&#xff0c;则可以考虑它的作用为滤波电容&am…

python爬虫实战——获取酷我音乐数据

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 开发环境: 版 本&#xff1a; python 3.8 编辑器&#xff1a;pycharm 2022.3.2 模块使用: requests >>> pip install requests 如何安装python第三方模块: win R 输入 cmd 点击确定, 输入安装命令 pip install…

职业身份来认同自己对吗?

人们常常以自己的职业身份来认同自己。这是一个巨大的错误。 你的职业身份只是一个外壳&#xff1b;它不能定义你是一个人。你可以期待你的职业媒介会随着时间而改变&#xff0c;但是你传达的信息会应该更加稳定。你的信息就是回答你是谁&#xff0c;你应该通过三十几年的工作…

linux中常用的命令

一&#xff1a;tree命令 &#xff08;码字不易&#xff0c;关注一下吧&#xff0c;w~~w) 以树状形式查看指定目录内容。 tree --树状显示当前目录下的文件信息。 tree 目录 --树状显示指定目录下的文件信息。 注意&#xff1a; tree只能查看目录内容&#xff0c;不能…

【Axure高保真原型】随机抽取案例

今天和大家分享随机抽取点餐案例的原型模板&#xff0c;包括2种效果&#xff0c;第一种是手动暂停效果&#xff0c;点击开始后随机抽取食物&#xff0c;手动点击暂停按钮后停止&#xff1b;第二种是自动暂停效果&#xff0c;点击开始按钮后随机抽取食物&#xff0c;并且开始倒计…

webassembly003 whisper.cpp的python绑定实现+Cython+Setuptools的GUI程序

CODE python端的绑定和本文一样&#xff0c;还需要将cdef char* LANGUAGE b’en’改为中文zh&#xff08;也可以在函数中配置一个参数修改这个值&#xff09;。ps:本来想尝试cdef whisper_context* whisper_init_from_file_with_params_no_state(char*, whisper_full_params)…