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

news2024/11/17 17:43:13

前言:在前面我们讲解了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更加安全和可读性更好.


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


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


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

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

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

相关文章

高分文献解读|乳酸通过与可溶性腺苷酸环化酶结合调控铁代谢

乳酸(LA)的过量产生可能发生在运动期间或者许多疾病中&#xff0c;例如癌症中。个人伴有高乳酸血症的患者常表现为贫血、血清铁减少以及一种铁代谢关键调控因子—铁调素&#xff08;hepcidin&#xff09;升高。然而&#xff0c;目前尚不清楚乳酸是否以及如何调节铁调素的表达。…

Android Clear架构最强官方指南Kotlin版

Android Clear架构最强官方指南Kotlin版 在这篇文章中&#xff0c;我将介绍关于Android应用程序架构的一些内容。尽管自从早期更稳健的Android架构方法在移动开发中变得流行以来已经说了很多话&#xff0c;但改进和演进的空间总是存在的。 基于上述文章中的清晰架构示例&…

证券公司怎么选择?福州开股票账户佣金最低是多少?怎么开低佣金账户?

股票交易佣金是指投资者在进行股票交易时&#xff0c;需要向券商支付的手续费。具体的佣金费用根据券商的政策而有所不同&#xff0c;一般分为固定佣金和按比例佣金两种方式。 固定佣金是指交易每一笔固定收取一定金额的佣金&#xff0c;通常适用于较小交易量的投资者&#xf…

EasyExcel重复多次写入,合并单元格功能详细实现

❤ 作者主页&#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是李奕赫&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习!!!&#x1f389;&#x1f389; 文章目录 EasyExcel如何…

TensorFlow Lite中文本分类在Android上的实践

#1 Tensorflow Lite TensorFlow Lite(后续简称TFL) 是 Google 开发的一个用于移动设备和嵌入式设备的开源库,旨在为移动终端设备提供机器学习推断。它是 TensorFlow 框架的轻量级版本,专门优化了模型的大小和性能,以适应资源受限的移动设备和嵌入式系统。 TFL 提供了一种在移…

力扣hot100 每日温度 单调递减栈

Problem: 739. 每日温度 文章目录 思路复杂度&#x1f49d; 单调栈 思路 &#x1f469;‍&#x1f3eb; 参考题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) &#x1f49d; 单调栈 class Solution {public int[] dailyTem…

贝锐蒲公英全新网页认证,保障企业访客无线网络安全

随着企业规模的不断扩大、人员的增长、无线终端数量/类型的增加&#xff0c;传统WiFi无线网络会暴露出越来越多的问题&#xff0c;导致无线网络管理困难。 比如&#xff1a;采用弱密码、安全防护不到位的默认设置、员工缺乏信息安全意识、未经授人员权访问无线网络…… 这些问…

力扣日记1.28-【回溯算法篇】93. 复原 IP 地址

力扣日记&#xff1a;【回溯算法篇】93. 复原 IP 地址 日期&#xff1a;2023.1.28 参考&#xff1a;代码随想录、力扣 93. 复原 IP 地址 题目描述 难度&#xff1a;中等 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&…

(蓝桥杯每日一题)求最长回文串

问题描述 给出一个长度为 n 的小写字符串&#xff0c;求一个最长的子串 S&#xff0c;满足SXY,X&#xff0c;Y>1&#xff0c;且X,Y 均为回文串。 输入格式 输入包括一行: 第一行是一个长度为 n 的小写字符串。 输出格式 输出包括一行&#xff1a; 一行一个整数&#xff0c;表…

数据湖技术之应用场景篇

数据湖技术有较多的应用场景&#xff0c;本篇文章是针对一些典型的痛点场景做了一些介绍和说明。比如说在线数据抽取场景原有模式对线上库表产生较大压力&#xff0c;flink多流join维护的大状态导致的稳定性问题等等&#xff0c;具体场景如下图所示&#xff1a; 场景1:在线数据…

Mysql-InnoDB-数据落盘

概念 1 什么是脏页&#xff1f; 对于数据库中页的修改操作&#xff0c;则首先修改在缓冲区中的页&#xff0c;缓冲区中的页与磁盘中的页数据不一致&#xff0c;所以称缓冲区中的页为脏页。 2 脏页什么时候写入磁盘&#xff1f; 脏页以一定的频率将脏页刷新到磁盘上。页从缓冲区…

十大排序算法之冒泡排序

冒泡排序 冒泡排序是最基础的排序算法之一&#xff0c;但它并不是最优的排序算法。 1. 算法思想 冒泡排序是将数据中的每个数与相邻数进行比较并交换&#xff0c;大数往上冒&#xff0c;小数往下沉&#xff0c;每个数都遍历一次后便可以排出顺序。 2. 算法步骤 &#xff0…

【极数系列】Flink环境搭建Linux版本 (03)

文章目录 引言01 Linux部署JDK11版本1.下载Linux版本的JDK112.创建目录3.上传并解压4.配置环境变量5.刷新环境变量6.检查jdk安装是否成功 02 Linux部署Flink1.18.0版本1.下载Flink1.18.0版本包2.上传压缩包到服务器3.修改flink-config.yaml配置4.启动服务5.浏览器访问6.停止服务…

R语言学习case6:ggplot基础画图(Scatter散点图)

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;利用ggplot工具包绘图 plot1 <- ggplot(iris…

5G_RACH(一)

什么是RACH RACH 代表 Random Access Channel。这是开机时UE发给eNB的第一条消息。 为什么选择RACH &#xff1f;&#xff08;RACH 的功能是什么&#xff1f; 当你第一次听到RACH或RACH Process这个词时&#xff0c;你脑海中浮现的第一个问题是“为什么是RACH&#xff1f;”…

蓝桥杯-循环节长度

两个整数做除法&#xff0c;有时会产生循环小数&#xff0c;其循环部分称为: 循环节。比如&#xff0c;11/136>0.8461553846153..... 其循环节为[846153] 共有 6 位。下面的方法&#xff0c;可以求出循环节的长度。请仔细阅读代码&#xff0c;并填写划线部分缺少的代码。 注…

贪吃蛇游戏的实现

一.技术要点: 贪吃蛇需要掌握: c语言函数,枚举,结构体,动态内存管理,预处理指令,链表,Win32 API等 二.Win32 API 1.Win32 API简介 windows可以帮应用程序卡其视窗,描绘图案,使用周边设备,,Win32 API就是windows32位平台上的应用程序编程接口 2.控制台程序 (1).使用cmd命令…

Matlab绘图技巧-NAN元素绘图出现锯齿状解决办法

Matlab绘图技巧-NAN元素绘图出现锯齿状解决办法 想必有很多同学遇到绘制3维曲面热力图&#xff0c;有一些数据是nan&#xff0c;绘制出来的图会出现锯齿状&#xff1a;如下图&#xff1a;    如果用matlab直接绘制带nan的矩阵的话&#xff0c;则会像上图一样&#xff0c;当然…

Flink Checkpoint 超时问题详解

第一种、计算量大&#xff0c;CPU密集性&#xff0c;导致TM内线程一直在processElement&#xff0c;而没有时间做CP【过滤掉部分数据&#xff1b;增大并行度】 代表性作业为算法指标-用户偏好的计算&#xff0c;需要对用户在商城的曝光、点击、订单、出价、上下滑等所有事件进…

数据变换

数据变换 对数变换傅里叶变换Box-Cox 变换 对数变换 对数变换是一种常见的数据变换方法&#xff0c;它将原始数据取对数&#xff0c;常用的是自然对数&#xff08;以 e 为底&#xff09;或者以 10 为底的对数。对数变换的作用包括&#xff1a; 降低数据的尺度&#xff1a;对数…