C++语言·入门

        现在我们讲完了数据结构初阶部分的内容,数据结构剩下的内容会在C++语言讲解的差不多的时候加入。

1. 什么是C++

        C语言是结构化模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度抽象和建模时,C语言则不合适。为了解决软件危机,20世纪80年代,计算机界提出了OPP(object oriented programming:面对对象)思想,支持面向对象的程序设计语言应运而生。

        1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩大了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此,C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

        可能我们还见过C#语言,值得注意的是这个根C/C++没有关系,它是微软公司搞出来对抗Java语言的,所以它对标的是Java。

2. C++的发展史

        1979年,贝尔实验室的本贾尼等人试图分析unix内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为 C with classes

        C++不断发展的道路上出现了众多的版本,目前最新的版本发展到了 C++23 ,但是目前主流的版本还是C++98和C++11,所以大家也不用一味的追求最新

3. 命名空间

        在C/C++中变量、函数和后面学到的类都是大量存在的,这些变量、函数、和类的名称将都存在于全局作用域中,可能会导致很多冲突,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字的污染,namespace关键字的出现就是针对这类型的问题的

        比如我们现在定义一个全局变量rand,并打印它,目前程序没有任何问题

        

        此时我们引入stdlib.h头文件,现在rand变量和rand()函数发生了冲突,程序报错,其原因就是出现了命名冲突的问题

        

        那么C++中的命名空间就可以很好的解决这一问题,在此之前我们先说一个操作符

        我们直到在同时定义了全局变量和局部变量的时候,程序会使用局部变量,那么此时如果我们还是想用全局变量的时候就要用到域作用限定符 :: ,当域作用限定符前面为空时默认是访问全局域

        那么在使用命名空间的时候我们也可以用这一方法避免变量名的冲突:

                      

        当我们使用一个变量、函数时编译器默认的查找顺序是:先在当前局部域找,再到全局域找,值得注意的是默认情况下不会去命名空间去找。可以说如果我们用了域作用限定符,那么就会直接到对应命名空间查找,如果不用就不会访问命名空间

        命名空间中除了可以定义变量还可以定义函数和类型,这样可以避免和全局域,或者他人的命名空间的内容冲突

        同时命名空间还可以嵌套

                

        值得注意的是结构体类型在使用的时候命名空间的名称写在struct之后

        在同一项目的不同文件中可以使用同名的命名空间,若如此做,编译器会自动将几个文件的相同命名空间合并

                                

        像这样我们就将add函数和sub函数的声明也放进了命名空间a当中,但是我们注意到这两个函数现在的状态是未找到定义,再运行一下也会发现有如下报错

        这提醒了我们,当把这两个函数声明放到命名空间a中后,它们的名字就应该变成了 a::add ,但是我们在定义它们的时候没有写 a:: 这样的结果就是没有 a::add 的定义,只有 add 的定义。

        解决方法有两种,第一种就是在前面加上域作用限定,但是这种方案无疑太过繁琐,在函数很多的情况下就太麻烦了。第二种解决方案就是将函数的定义也放进命名空间中,这样声明和定义都在一个空间中,自然就能定义上了

        那么如果我们嫌弃每次使用命名空间的时候都要在前面指定命名空间太麻烦怎么办,此时我们可以使用展开命名空间的方式避免。

        展开命名空间,可以理解成将命名空间中的所有内容粘贴到 using 语句的所在位置,和#include的功能相似。但不完全是这样,那些同名的变量或者函数的身份还是在不同的域中,也就是说,只要我们不使用这些变量或者函数,只是将它们各自的作用域展开,是不会造成因引用歧义而导致的报错的。

                

        就像这样,即使我们没有在前面限定命名空间,但是第二句代码还是正确的执行了,但是我们要注意的是在展开命名空间的时候不要有其他重名的内容,无论是在全局变量还是其他被展开的命名空间中,否则还是会造成访问冲突。当然,如果有重名的局部变量,就按局部变量走了,不会造成访问冲突。

        展开命名空间还有一种单独展开命名空间中的某个变量的玩法

                        

        我画红直线的语句就是单独展开了a命名空间中的 rand 变量,但是 rand1 没有被展开,所以不能直接使用,因此报错了。可以注意一下,展开命名空间和展开命名空间中的一个变量的语句是有区别的,具体来说就是有无namespace

4. C++输入输出

        C++的输入输出方式放在<iostream> (in out stream) 流式输入输出库当中,其输入函数是cin,输出函数是cout,c指的是conslo就是控制台的意思,在输入输出的时候需要用到 >> 和 << 流提取、流插入,这个东西与C语言不同的是它可以自动识别类型,而且你可以随便写,不用注重格式什么的,输出的最后换行用 \n 或者 endl (end line) 都可以他俩是等价的。

        C++的标准库为了防止库里的内容和我们自己定义的东西冲突,所以将库里的内容包到了std命名空间中,因此我们想使用C++标准库里的东西的时候要记得展开一下这个库的命名空间,当然你可以完全展开,也可以只展开 cin cout 

                ​​​​​​​

        当然,因为C++对于C的兼容性,你也可以在iostream头文件下使用printf和scanf函数进行格式化输入输出,格式化的一个最基本好处就是方便控浮点数的精度。当然流式输入输出也能控制精度,就是麻烦一点

5. 缺省参数

        缺省参数是指在声明或定义一个函数时,为函数的参数指定一个缺省值。在调用该函数的时候,如果没有指定实参则采用该缺省值。

        ​​​​​​​        ​​​​​​​        

5.1 全缺省参数

        顾名思义,就是将函数的所有参数进行缺省预设。

        但是要注意在为函数传参的时候必须按从左向右的顺序传参,不能跳着传

5.2 半缺省参数

        这个就是可以只给后几个预设置缺省值,同样要注意非缺省参数只能在前面,否则将引起歧义。同时半缺省参数也不能跳着设置,非缺省参数必须从左向右连续排列,剩下后半段全都是缺省参数。

        那么像这种半缺省的函数就不能啥参数都不传了,至少要把那些没有预设值的参数填上

        这里还要注意一个点,就是缺省参数不能在函数声明和定义中同时给,因为怕你两个值给的不一样呗,那到底该听谁的。所以缺省参数只能给在一个上面,当然一般是给在声明上面为主。

6. 函数重载

        函数重载是函数的一种特殊情况,C++允许在同一作用域下声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题

        ​​​​​​​        

        这段代码中俩个Swap函数符合同一作用域,不同形参列表的要求,因此它们满足函数重载要求,并且事实上,它们确实重载了。编译器根据传过来的参数类型,智能的选择了对应参数类型的函数。这个功能还是相当实用的,看起来用的就是一个函数名,但是可以实现不同类型参数的相似函数功能,回想一下上次在C语言中我们实现类似功能的时候用的还是宏的时候有一个##运算符,emmm……好像也不是太像啦

C语言·预处理详解_c语言 复杂预处理应用-CSDN博客文章浏览阅读1.2k次,点赞24次,收藏29次。本节讲解了预定义符号的含义。#define定义常量或定义宏的用法和注意事项,关键是其暴力替换的原理可能产生的问题以及解决办法。在定义宏中#和##的意义。#undef取消宏定义。命令行定义不确定大小的参数。条件编译的用法。头文件不同包含方式的意义原理,以及头文件重复包含的危害和解决办法_c语言 复杂预处理应用https://blog.csdn.net/atlanteep/article/details/135648080?spm=1001.2014.3001.5501

        在不同命名空间中的同名函数,在两个命名空间同时展开在同一域之中的情况下,这俩个函数也会产生函数重载,此时再加上缺省参数就会变得更加好玩了

        ​​​​​​​        ​​​​​​​        

        现在可以思考一个问题,如果只给a2中的函数搞上缺省参数,不给a1中的搞了会发生什么,程序会报错吗?还能不能不传参数?还能不能只传一个参数?如果用函数重载的方式还能不能访问到a1中的函数,如果不能,我们该用什么方案使用a1中的函数?

        这些问题最终都导向一点,就是在重载函数的时候不要搞出引起歧义的东西。

        C语言不支持函数重载但是C++支持,究其本源是因为在编译链接的过程中,C程序中需要调用函数的时候会直接用函数名去查找,而C++程序需要调用函数的时候会用修饰后的函数名去查找。修饰的意思可以简单理解成,在汇编的过程中会将该函数的形参信息与函数名放在一起,形成一个修饰后的函数名,这个修饰后的函数名我们可以在汇编代码中见到。

7. 引用

7.1 引用的概念

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

        类型& 引用变量名(对象名) = 引用实体

        引用特性:

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

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

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

        通过这段代码我们可以感受到一个变量可以被多次引用、引用可以被引用、所有引用和它对应的实体共用一块空间,随便改变其中一个引用,其他引用和实体也会改变。       

7.2 常引用

        引用不能引用一个常量,因为这造成了权限放大,也就是说,如果能引用的话,难道我们可以容忍通过引用来改变它的实体吗?

        说到底常引用的问题就是一个权限放大(不能容忍),权限平移(可以容忍),权限缩小(可以容忍)

        观察上面这段代码,权限放大和平移就不讲了。我们观察权限缩小了之后,k不能改变了,但是我们可以通过它的本体r间接的改变k,,甚至还可以用其他有权限的r的别名间接改变r和k,这就是权限缩小。

         我们观察下面一段代码

                ​​​​​​​        

         我们先看第一组,将一个double类型的变量赋值给一个int类型的变量,在赋值的过程当中发生了隐式类型转换,毋庸置疑这样做是没有问题的。

        第二组,将double类型的变量引用给int类型报错了,但是引用给const int类型变量就行。这是因为在变量赋值时同时发生了类型转换,那么编译器就会先将赋值的结果先放到一个临时变量中去,在这里就是3会先被放到一个临时变量里面去,这个临时变量是具有常性的,就相当于是被const修饰过的,之后再把这个临时变量的值再给给别名。这样就解释了为什么p1报错了,因为发生了权限放大。无论是隐式类型转换还是强制类型转换都一样会产生临时变量。

        第三组报错的原理和第二组一样,加法计算完后的结果也会先放到一个具有常性的临时变量中去。如果别名不用const修饰,那么就会发生权限放大。只要是表达式运算就会产生临时变量。

7.3 使用场景

        做参数

        ​​​​​​​        

        之前我们想要交换两个变量需要传递他俩的指针,但是我们可以用将形参变成实参的别名简化这一操作,这样我们改变函数形参就能影响到实参了。

7.4 引用和指针的区别

        1. 引用概念上定义了一个变量的别名,指针存储一个变量的地址

        2. 引用在定义时必须初始化,指针没有要求

        3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

        4. 没有NULL引用,但又NULL指针

        5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)

        6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

8. 内联函数

        以 inline 修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数可以提升程序的运行效率,其效果和宏函数是一样的,但是宏函数要写那么多括号很容易出错。

        ​​​​​​​        ​​​​​​​        

        上面这段代码就是利用了内联函数和引用写出来的一个交换函数,像我们在写排序那节中的代码的时候,可能要执行成百上千次的交换函数,如果每次调用都要建立函数栈帧,那消耗未免太大,所以我们可以用内联函数的方式提升代码运行效率。同时我们发现内联函数的写法很简单,就是在函数前面加上个inline,比宏函数简单实用太多了。

        如果你想通过观察汇编代码看内联函数的执行方案的话不要用debug模式,要用release模式,然后如果有call Swap就说明call这个函数了码,就是建立栈帧了。

        如果我们尝试把内联函数中的代码量加大的话就会发现,汇编代码中还是call这个函数了,这是正常的,因为inline对于编译器就是个建议,将函数规模较小、不是递归且频繁调用的函数采用inline修饰,否则编译器会忽略inline的特性。

        如果我们给的内联函数比较大,比如100行,如果我们要调用1W次。用内联函数把函数中的操作粘贴到指定位置的方式我们的汇编代码中需要100*1W行。那么如果用函数调用的方式就只用100+1W行就够了,因为每次调用只用写一行call这个函数就行。那么汇编代码最终就能影响到生成可执行程序的大小,汇编代码量越小自然可执行程序越小。所以说编译器不将inline的权限完全放给我们,最终还是让它自己决定是否将这个函数搞成内联的。

        最后内联函数不能声明和定义分离,就是说不能像之前那样在头文件中写它的声明,在对应源文件中写它的定义,这样操作会导致链接错误,因为内联函数不需要call所以在符号表中不会生成它的地址,此时我们从主函数中调用就相当于通过头文件中的声明去call它的地址,但是符号表中压根就没有它的地址,所以就会链接错误。那么正确的做法就是把声明定义都放到头文件中就好了,这样include头文件就直接把内联函数整体都粘过去了,就没有问题了。

9. auto关键字

        auto作为一个新的类型类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

        这里的 typeid(变量).name() 就是取这个变量的类型的函数,我们可以看出来,auto自动识别出来的结果正是我们想要得到的结果,并且auto还能识别函数返回值的类型。

        注意:使用auto定义变量的时候必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译的期会将auto替换为变量实际的类型

9.1 auto的使用细则

        用auto声明指针类型时,用 auto 和 auto* 没有任何区别,但是用auto声明引用类型时则必须加&

                        

        在同一行定义多个变量时,这些变量必须是相同的类型,否则编译器会报错。因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

        auto不能推导的场景:1. auto不能作为函数的参数     2. auto不能直接用来声明数组

10. 范围for循环

        如果用C我们写一个循环是这样的

        

        但是在C++中我们可以使用范围for来遍历一个数组

                

        for循环后的括号由冒号 : 分成了两个部分,第一部分是范围内用于迭代的变量,第二部分是被迭代的范围,这个范围是定死的就是从数组的开始到结束的一个正序遍历。遍历的过程就是拿出arr中的一个值赋值给e然后进行大括号中的操作,范围for会自动进行初始化,结束判定,和++

        

11. 指针空指针nullptr (C++11)

        NULL其实是一个宏,在传统的C头文件(stddef.h)中,可以看到如下定义

        ​​​​​​​        ​​​​​​​        

        可以看到NULL可能被定义为字面上的常量0,也有可能被定义成无类型指针(void*)常量。无论采用那种定义方案,在使用空指针时,都可能遇到一些麻烦

        ​​​​​​​        ​​​​​​​        ​​​​​​​

        可以看到第二行的f函数在重载的时候,因为NULL被定义成0,所以结果偏离了我们的预想。

        所以说我们之后可以将NULL都换成nullptr关键字代替。

        注意:

                1. 因为nullptr被引入称为了关键字,所以说在使用的时候不用包含头文件

                2. 在C++11中,sizeof(nullptr) 和 sizeof((void*)0) 所占字节数相同,我们可以直接理解成他俩等价。

                3. 为了提高代码的健全性,在后续表示空指针的时候都建议使用nullptr

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

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

相关文章

软件测试-用例篇

目录 1 测试用例的基本要素2 测试用例给我们带来的好处3 测试用例的设计方法3.1 基于需求进行测试用例的设计3.1.1 功能需求测试分析3.1.2 非功能需求测试分析 4 具体的设计方法4.1 等价类4.2 边界值4.3 错误猜测法4.4 场景设计法4.5 因果图4.5.1 因果图需要掌握的基本知识4.5.…

用一个程序解决SQLite常见的各项操作(实用篇)

文章说明&#xff1a; 本篇文章是在之前的一篇文章SQLite3进行数据库各项常用操作基础上写的&#xff0c;将SQLite涉及到的常用的几种操作&#xff0c;以函数的形式处理成相互调用的形式。 因为之前的文章对基础操作已经解释过了&#xff0c;所以这里直接放置可执行代码和结果…

基于YOLOv8的绝缘子检测系统

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文摘要&#xff1a;基于YOLOv8的绝缘子小目标检测&#xff0c;阐述了整个数据制作和训练可视化过程 1.YOLOv8介绍 Ultralytics YOLOv8是Ultralytics公司开发的YOLO目标检测和图像分割模型的最新版本。YOLOv8是一种尖端的、最先进的&a…

kali Linux上安装docker过程记录

安装情况&#xff1a; 直接安装提示错误&#xff01;&#xff01;&#xff01; 安装程序命令&#xff1a; apt install -y docker.io 安装结果提示安装失败&#xff01;&#xff01;&#xff01;看别人安装直接成功到我这怎么失败&#xff01;&#xff01;&#xff01;找原因…

引用,内联函数,auto函数,指针nullptr

一&#xff1a;引用 1.1 该文章的引用是对上一篇引用的进行补充和完善 按理来说&#xff0c;double可以隐式转换为int&#xff0c;那起别名的时候为什么不可以类型转换呢&#xff1f; 那是因为&#xff0c;在类型转换的时候&#xff0c;会创建一个临时变量&#xff0c;让后再…

如何提高小红书笔记的收录率?

在小红书平台上&#xff0c;笔记的收录率是衡量一篇笔记是否受欢迎和有价值的重要因素。为了提高笔记的收录率&#xff0c;有几个关键点需要注意&#xff1a; 1.内容不涉及广告 在发布笔记前要先确保笔记内容不包含任何形式的广告或推广信息。小红书平台对于广告性质的内容有…

24年大一训练一(东北林业大学)

前言&#xff1a; 周五晚上的训练赛&#xff0c;以后应该每两周都会有一次。 正文&#xff1a; Problem:A矩阵翻转&#xff1a; #include<bits/stdc.h> using namespace std; int a[55][55]; int main(){int n,m;while(cin>>n>>m){for(int i1;i<n;i){for…

Mysql数据库:故障分析与配置优化

目录 前言 一、Mysql逻辑架构图 二、Mysql单实例常见故障 1、无法通过套接字连接到本地MySQL服务器 2、用户rootlocalhost访问被拒绝 3、远程连接数据库时连接很慢 4、无法打开以MYI结尾的索引文件 5、超出最大连接错误数量限制 6、连接过多 7、配置文件/etc/my.cnf权…

PDF转成二维码分享

在制作电子产品册之前&#xff0c;你需要思考以下几个问题&#xff1a;你的电子产品册是面向什么人群的&#xff1f;是宣传册、使用手册还是产品介绍册&#xff1f;明确目标与定位有助于我们更好地规划产品册的内容和风格。 一、收集素材与整理信息 在开始制作之前&#xff0c…

latex学习笔记

一 安装latex&#xff08;vscodetexlive&#xff09; 安装latex学习链接&#xff1a; 【超详细】最好用LaTex环境安装配置手把手教学&#xff01;&#xff01;&#xff08;支持双向搜索&#xff0c;附赠所需安装包及竞赛模板&#xff09;_哔哩哔哩_bilibilihttps://www.bilib…

KeepAlived使用介绍

目录 1、Introduce 2、基本使用 &#xff08;1&#xff09;安装 &#xff08;2&#xff09;配置文件 &#xff08;3&#xff09;使用教程 1、Introduce keepalived是一个用于实现高可用性和负载均衡的开源软件。它提供了一种轻量级的方式来管理多个服务器&#xff0c;并确保…

高效解决Ubuntu Server 18.04.1 LTS 64bit更新gdb8.1.1到gdb12.1

文章目录 问题解决步骤 问题 因为需要用到gdb一些指令&#xff0c;但是gdb8.x好像存在普遍的问题&#xff0c;实现不了某些指令&#xff0c;比方说set detach-on-fork on&#xff0c;升级版本也没有比较好的教程 经过我不断的试错&#xff0c;我终于升级成功了&#xff01;&a…

【学习笔记】java项目—苍穹外卖day02

文章目录 苍穹外卖-day02课程内容1. 新增员工1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.1.3 表设计 1.2 代码开发1.2.1 设计DTO类1.2.2 Controller层1.2.3 Service层接口1.2.4 Service层实现类1.2.5 Mapper层 1.3 功能测试1.3.1 接口文档测试1.3.2 前后端联调测试 1.4 …

[C++11]可变参数模板

导览&#xff1a; 本章将从可变参数模板的概念开始讲起&#xff0c;到其究竟是如何做到实例化的再从实例出发&#xff0c;探究该如何编写可变参数模板最后涉及可变参数模板的运用 什么是可变参数模板 让我们先见一下可变参数模板 template<typename ...Args> void te…

【SpringCloud】一文详谈Nacos

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》《项目实战》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 …

Linux之用户账号、用户组和与账号有关的系统文件

目录 一、基本介绍 1.用户和用户组 2.UID和GID 二、 账户管理 1.查看用户的UID和GID 2.添加账户 3.删除账号 4.修改账号 5.账户口令 三、分组管理 1.新增用户组 2.删除用户组 3.修改用户组 4.用户组切换 四、与账号有关的系统文件 1./etc/passwd 2./etc/shado…

李宏毅深度强化学习导论——当奖励是稀疏的

引言 这是李宏毅强化学习的笔记&#xff0c;主要介绍如何处理稀疏奖励问题。 稀疏奖励 当我们拿Actor和环境互动后可以得到很多奖励&#xff0c;整理之后可以得到分数 A A A&#xff0c;然后可以训练Actor。 但RL中有时会出现多数情况下奖励为零&#xff0c;此时我们不知道动…

行存储与列存储:大数据存储方案的选择与优缺点分析

随着大数据时代的来临&#xff0c;数据的规模和复杂性呈指数级增长&#xff0c;传统的关系数据库已经不再适应这一巨大的存储量和计算要求。在大数据存储领域&#xff0c;行存储和列存储成为两种备受关注的存储方案。本文将探讨行存储和列存储的定义、优缺点&#xff0c;并结合…

第十四届省赛大学B组(C/C++)岛屿个数

目录 题目链接&#xff1a;岛屿个数 解题思路&#xff1a; AC代码&#xff08;BFSDFS&#xff09;&#xff1a; 题目链接&#xff1a;岛屿个数 小蓝得到了一副大小为 MN 的格子地图&#xff0c;可以将其视作一个只包含字符 0&#xff08;代表海水&#xff09;和 1&#xff0…

LeetCode-331. 验证二叉树的前序序列化【栈 树 字符串 二叉树】

LeetCode-331. 验证二叉树的前序序列化【栈 树 字符串 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;看提示主要是栈和树。这题其实不是二叉树的遍历题&#xff0c;而是检验二叉树基础知识的题&#xff0c;也许有些难想。第一种解法是&#xff1a;把有效的叶子节点使用…