C到C++的敲门砖-2

news2024/11/10 12:57:22

文章目录

  • 引用
  • 内联函数
  • auto关键字
  • 基于范围的for循环
  • 指针空值nullptr
  • 后记

引用

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

所谓引用就是给变量起别名,在语法上来说是不会开辟变量的。
例如:

int main()
{
	int a=0;
	int&ra=a;
	a=5;
	cout<<a<<endl;
	cout<<ra<<endl<<endl;
	ra=10;
	cout<<a<<endl;
	cout<<ra<<endl;	
}

在这里插入图片描述
如上,即为代码的运行结果。可以看到我们改变了a的值,ra也跟着改变。改变了ra的值,a也跟着改变。这个ra就类似于c语言的指针,但此处不同在于不需要解引用。
此外,引用还有以下特性:

  • 引用在定义时必须初始化
    这是很好理解的,如果不初始化就很容易有歧义:
int a=0;
int b=0;
int&ra;
ra=a;
ra=b;

如上,我们便没有对ra进行初始化,那么下面ra=a和ra=b,这到底是说ra是a的别名还是b的别名呢?所以,为了避免这样的歧义存在,引用在定义时必须初始化。

  • 引用一旦引用一个实体,再不能引用其他实体。原因还是为了避免歧义。这也是和指针不同的一点,这也导致有些场景只能用指针而不能用引用来解决
  • 一个变量可以有多个引用。
    一个人可以有多个别名,那么变量当然也可以有多个别名。

除了上面的特性外,使用引用还有以下需要注意的点:

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

上面我们了解了引用的定义,那么c++多出这么个概念又有什么作用呢?
别急,下面我们就来谈谈引用的使用场景:

  1. 做参数
void Swap(int& left, int& right)
 {
 int temp = left;
 left = right;
 right = temp;
 }
 int main()
 {
 	int a=10,b=5;
 	Swap(a,b);
 }

如上代码,我们以往使用c语言时传参就必须是穿址调用才能交换a,b的值。现在可以引用调用,只传a,b的值就能实现Swap。
这样做的一大好处就是增加了代码的可读性,不用时不时传一个指针、二级指针,不仅不好理解,传参的时候也麻烦。此外,由于引用实际上是给变量取别名,所以left和right实际上就是a和b,换句话说这两个变量不需要压栈,这就减少了空间开销。
不过话又说回来,引用的底层实际上还是用指针来实现的

  1. 做返回值
int& Count()
 {
 static int n = 0;
 n++;
 // ...
 return n;
 }

这里的返回值是n的一个引用,我们就可以通过改变返回值来改变n的值。但是正如指针会有野指针,引用也有野引用。引用做返回值的时候,需要注意返回的对象没被销毁。因此只有堆、静态区、全局变量才能返回引用。

学习了引用,细心的读者也可以发现这简直和指针太像了,一股子指针味。这下谁还分得清什么是指针,什么是引用啊!无妨,接下来我们谈谈引用和指针的区别:

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

阅读上面的指针和引用的区别,我们也了解到了引用的一些不足之处,但是总体来说,相较于指针,引用还是更方便的。

内联函数

学习内联函数前,我们要知道编译器调用函数时要进行申请栈帧、压栈、弹栈等操作的
如果一段较为简短的代码在程序里反复调用,那么上述过程产生的开销就很大。因此在c++中专门用一个关键字inline修饰内敛函数,来达到类似宏定义函数的效果,从而解决上述问题。
如下:

inline int Add(int a=0,int b=0)
{
	return a+b;
}
int main()
{
	Add();
	Add();
	Add();
	Add();
	Add();
	Add();
	Add();
	Add();
	Add();
	return 0;
}

如上函数,如果不加上inline修饰,那么Add调用消耗栈帧就比较大。现在用inline修饰后,Add这个函数会进行代码块展开,这用就不用进行开辟栈帧、压栈、弹栈等操作。
inline的特性:

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

auto关键字

随着程序逐渐复杂,变量类型也逐渐复杂。就算是一个简单的函数int Add(int ,int ),其函数指针的类型为int (*)(int ,int ),这也是比较复杂的。虽然可以通过typedef来进行一定程度的改善,但是就算是使用typedef也是比较繁琐的。
为了解决上述的问题,c++使用auto关键字来自动识别变量类型。
如下:

int TestAuto()
 {
 return 10;
 }
 int main()
 {
	 int a = 10;
	 auto b = a;
	 auto c = 'a';
	 auto d = TestAuto();
	 cout << typeid(b).name() << endl;
	 cout << typeid(c).name() << endl;
	 cout << typeid(d).name() << endl;
	 return  0;
 }

上面代码的b,c,d都用了auto来自动识别类型,其中b为int,c为char,d为int。是不是感觉方便了不少呢?
需要注意的是,使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
auto的使用场景:

  • auto与指针和引用结合起来使用,用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&。(当然auto*声明的变量必须是指针类型,而auto声明的变量类型既可以是指针类型,也可以不是指针类型)
  • 在同一行定义多个变量
    当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
    器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
void TestAuto()
 {
	auto a = 1, b = 2; 
	auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

auto不能使用的场景:

  • auto不能作为函数的参数
  • auto不能直接用来声明数组
void TestAuto()
 {
	 int a[] = {1,2,3};
	 auto b[] = {456};//不可行
 }

基于范围的for循环

范围for循环:
一个已知范围的数组可以用如下的遍历方式:

int main()
{
	int arr[]={1,2,3,4,5,6,7,8,9,10};
	for(int i=0;i<sizeof(arr)/sizeof(int);i++)
		arr[i]++;
	return 0;
}

对于一个已知范围的数组,需要遍历其元素时用上述方式显得繁琐,而且还是错误的风险。
在c++中,我们就可以使用下面的方式来遍历数组元素:

void TestFor()
 {
	 int array[] = { 1, 2, 3, 4, 5 };
	 for(auto& e : array)
	 	e *= 2;
	 for(auto e : array)
	 	cout << e << " ";
	 return;
 }

在这里插入图片描述
就这样,我们使用了一种较为简洁的方式来遍历有范围的数组。
范围for的使用条件:

  • for循环迭代的范围必须是确定的。对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
void TestFor(int array[])
 {
	 for(auto& e : array)
	 cout<< e <<endl;
 }

上述代码就是一个典型的使用错误,因为函数压栈时是绝对不会传数组的,这样的空间开销太大。所以这里的array是一个指针,因此array的范围并不明确。

  • 迭代的对象要实现++和==的操作。

指针空值nullptr

在C语言中我们常用NULL来赋值空指针,但实际上NULL是一个宏定义的值,本质是int 0.
前面我们学习了函数重载,由此就引出了一个问题。

void Test(int x)
{
	cout << "int" << endl;
}
void Test(int* x)
{
	cout << "int*" << endl;
}
int main()
{
	Test(0);
	Test(NULL);
	return 0;
}

上述代码的理想的输出结果为:
int
int*
但实际的输出结果为:
在这里插入图片描述
也就是我们的NULL被识别成了int类型,这和我们的本意显然是冲突的。
由此,c++引入了指针空值nullptr。
注意:

  • 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  • 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  • 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

后记

以上就是大致的C和C++的一些用法上的不同,显然C++的各种语法改良了C语言很多不足的地方,这也为我们叩响了C++的敲门砖。就借着此处的优势继续向着C++的更深处进发吧。

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

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

相关文章

基于消失点的相机自标定

基于消失点的相机自标定 附赠最强自动驾驶学习资料&#xff1a;直达链接 相机是通过透视投影变换来将3D场景转换为2D图像。在射影变换中&#xff0c;平行线相交于一点称之为消失点。本文详细介绍了两种利用消失点特性的标定方法。目的是为根据实际应用和初始条件选择合适的标…

phpStudy安装thinkCMF8时,如何解决服务器rewrite和APIrewrite不支持的问题

解决步骤&#xff1a; 一&#xff1a;服务器rewrite 点击后面的问号跳转到官方文档链接&#xff1a; 复制红框内的代码 打开phpstudy&#xff0c;找到配置的站点&#xff0c;点击管理&#xff0c;找到伪静态 点击确认保存即可。 phpstudy会自动重启站点。 此时&#xff0c;…

CTF 题型 SSRF攻击例题总结

CTF 题型 SSRF攻击&例题总结 文章目录 CTF 题型 SSRF攻击&例题总结Server-side Request Forgery 服务端请求伪造SSRF的利用面1 任意文件读取 前提是知道要读取的文件名2 探测内网资源3 使用gopher协议扩展攻击面Gopher协议 &#xff08;注意是70端口&#xff09;python…

RPC学习笔记一

什么是RPC RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种用于实现分布式系统中不同计算机或进程之间进行通信和调用的技术和模式。 在传统的过程调用中&#xff0c;当一个程序需要调用另一个程序的函数或方法时&#xff0c;通常是在同一台…

【10】进阶常用第三方库

第三方库概览 名称文档一句话介绍jQuery官网:https://jquery.com/中文网:https://jquery.cuishifeng.cn/让操作 DOM 变得更容易Lodash官网:https://lodash.com/docs中文网:https://www.lodashjs.com/你能想到的工具函数它都帮你写了Animate.css官网:https://animate.style…

图算法总结

BFS: 基于队列, 入队列的顶点先被探索. DFS: 基于栈, 通过将顶点存入栈中, 顶点是沿着路径被探索的, 存在新的相邻顶点就去访问.深度优先遍历类似于树的先根遍历(先序遍历)

大模型提示学习样本量有玄机,自适应调节方法好

引言&#xff1a;探索文本分类中的个性化示例数量 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;预测模型已经从零开始训练演变为使用标记数据对预训练模型进行微调。这种微调的极端形式涉及到上下文学习&#xff08;In-Context Learning, ICL&#xff09;&…

由浅到深认识Java语言(1):前提概要

该文章Github地址&#xff1a;https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

外贸网站常用的wordpress模板

零件配件WordPress外贸建站模板 汽车行业零配件WordPress外贸建站模板&#xff0c;卖配件、零件的外贸公司可以使用的WordPress主题。 https://www.jianzhanpress.com/?p4912 WordPress外贸独立站主题 简洁实用的WordPress外贸独立站主题&#xff0c;适合时尚服装行业搭建w…

哈尔滨华时信息公司的校企合作怎么样

在当今竞争激烈的就业市场中&#xff0c;大学生的技能培养对于他们的未来职业发展至关重要。哈尔滨华时信息技术有限公司与黑龙江农业工程职业学院信息工程院的合作&#xff0c;无疑是一次具有深远意义的举措。这样的校企合作模式不仅符合国家政策导向&#xff0c;更带来了多方…

第四百零九回

文章目录 1. 概念介绍2. 方法与细节2.1 获取方法2.2 使用细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取当前系统语言"相关的内容&#xff0c;本章回中将介绍如何获取时间戳.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

java新手练习-类的继承调用(接口,抽象类,构造函数)和枚举定义方法

1.继承 父类可以调用子类实例对象,(对于调用抽象类必须使用继承) 模版 //抽象定义一个父类抽象类 abstract class FatherAbstract{void showFatherAbstract(String color, String name, int age, double serail_abstract){System.out.println("color " color &…

鸿蒙App开发学习 - TypeScript编程语言全面开发教程(上)

背景 根据鸿蒙官方的说明&#xff1a; ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集。因此&#xff0c;在学习ArkTS语言之前&#…

我的尝试:Codigger + Vim

若您愿意耐心投入&#xff0c;学习 Vim 的过程其实远比想象中轻松。我对 Vim 产生兴趣&#xff0c;主要是源于它对提升生产力的巨大潜力。我尝试了 Neovim、NvChad 以及 Codigger Vim 插件&#xff0c;如今我的工作效率已远超从前。 那么&#xff0c;Vim 究竟是什么呢&#xff…

交叉注意力融合时空特征的TCN-Transformer并行预测模型

独家 | 高创新预测模型 往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较-CSDN博客 风速预测&#xff08;一&#xff09;数据集介绍和预处理-CSDN博客 风速预测&#xff08;二&#xff09;基于Pytorch的EMD-LSTM模型-CSDN博…

OSPF虚链路vlink

OSPF虚链路——vlink&#xff08;使得其他区域和骨干区域相连&#xff09; 虚链路&#xff1a;一定是跨越非骨干区域连接的 1、虚链路属于区域0的逻辑链路 2、虚链路只能穿越1个非骨干区域 3、虚链路不能穿越特殊区域&#xff1b; vlink在配置的时候&#xff0c;需要指对方…

helm部署hadoop

&#xff08;作者&#xff1a;陈玓玏&#xff09; 参考helm仓库的文档&#xff1a;https://artifacthub.io/packages/helm/apache-hadoop-helm/hadoop helm helm repo add pfisterer-hadoop https://pfisterer.github.io/apache-hadoop-helm/ helm install hadoop pfistere…

IDEA中的Project工程、Module模块的概念及创建导入

1、IDEA中的层级关系&#xff1a; project(工程) - module(模块) - package(包) - class(类)/接口具体的&#xff1a; 一个project中可以创建多个module一个module中可以创建多个package一个package中可以创建多个class/接口2、Project和Module的概念&#xff1a; 在 IntelliJ …

基于Spring Boot的宿舍管理系统

摘 要 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建宿舍管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种楼宇信息、宿舍信息、宿舍安排、缺勤信息…

Android视角看鸿蒙第六课(module.json5中的各字段含义之pages)designWidth的用法

Android视角看鸿蒙第六课(module.json5中的各字段含义之pages&#xff09; 导读 前面几篇文章&#xff0c;我们陆续分析了entry->src->main下的module.json5中的各个字段的含义及作用。目前剩余pages和abilities两个字段&#xff0c;本篇文章一起来了解pages。 过程有错…