当前位置: 首页 > article >正文

C++入门基础

一.C++的第一个代码: 

       在学习C语言的过程中我们第一次接触的代码就是"hello world"所以这里我们也来打印一下:

这里可以看到我创建的是.cpp文件也就是说是C++文件但是这里我是用C语言来打印的hello world,所以可以知道C++是兼容C语言的。

在这里展示一下C++的输出流是什么样的:

  

这里对于刚学C++可能有三个问题,一就是这个包含的是什么   二就是这个using namespace  std是什么含义,还有C++的输入输出流是什么样的。

那个在C语言是左移运算符,而在C++中是流输出运算符,所以语言不同有可能表达的含义不一样。

这三个问题在后面C++的学习中都会一一解答的。

二.namspace:

  1.namespace的价值:

  

这个rand的变量是处于全局变量当中的所以可以成功打印,而当我们包含stdlib.h这个头文件时,就会报错:

这是因为在stdlib的头文件中有命名为rand的函数,所以这里会导致命名冲突,所以c语言就无法解决这个问题,就要改名字,所以就很麻烦。

所以下面介绍C++的命名空间就可以解决这个命名冲突的问题

2.namespace的定义:

定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字然后接⼀对{}即可{}中即为命名空间的成员命名空间中可以定义变量/函数/类型等
namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下⾯的rand不在冲突了。
C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/
类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响
编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期。
所以根据这一条我们就可以让上面这个问题完美解决:
加了namespace之后我们可以发现没有报错了,但是打印的结果不是10,这时因为这个rand=10被作为一个域给封起来了,所以无法访问,此时访问的是stdlib头文件里的rand。这个命名空间是个域。
还有一个问题:
当都有a这个变量时,会先在局部找再在全局找,就近原则,所以这里会打印1.所以也就代表了不同的域可以定义同名变量
上述这两个照片中有两个问题,第一个图片中有一个问题是如何将rand的值打印出来,第二个是如何将全局变量的a打出来。
这两个问题可以很好解决,就用一个运算符 域的名称::变量名(::这个就叫域作用限定符)
这样问题就完美解决了。
当域作用限定符左边没有写域的名称时,默认访问的是全局变量里的成员:
这些域里面的成员变量虽然是被域给封锁起来了,但其还是全局变量。
上面说到域里面不仅可以定义变量还可以定义函数和类型(如结构体):
想要在域中利用函数也跟在域中找成员变量一样 利用域作用限定符:
想要利用域定义一个结构体怎么定义呢,同理也是用域作用限定符,但是因为struct是关键字所以::不加在struct前面:
namespace只能定义在全局,当然他还可以嵌套定义。
项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突。
C++标准库都放在⼀个叫std(standard)的命名空间中。
嵌套定义:
    域也可以嵌套定义:
       
这样也可以解决很多问题。
项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突:
这里定义了一个栈
不同文件中可以使用相同的域名称,这样是不会冲突的,并且在逻辑上编译器会自动合并在一起。
这就是域的好处与便利。
如果全局定义了一个栈,我们利用域也定义了一个栈,如何使用不同的栈呢:
也是利用域作用限定符:
利用全局的栈去定义时,全局的栈是静态的,10个整形数组是40个字节,top和空间都是4个字节所以一共是44个字节。
文件域空间里的栈是动态的一个指针4个字节,top和空间也是4个字节所以是12个字节
这样也更好的显示出域的好处,这样不会发生命名冲突。
C++标准库都放在⼀个叫std(standard)的命名空间中:
在上面我们介绍到了C++的输出流是cout<<  但在上面的代码中我们用了using  namespace std,如果我们不加这串代码,能否继续用输出流cout<<  ,如果不能用,该如何去解决呢:
                                     
很明显看到这里是报错了的,所以该如何去解决呢?
既然std是C++定义的标准库,所以这也是个域,并且这个域已经在包含的头文件当中了,所以我们只需要加上这个域作用符即可:
这样就完美解决了。

3.命名空间的使用:

   编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。所以下⾯程序会编译报错。所以我们要使⽤命名空间中定义的变量/函数,有三种⽅式:

指定命名空间访问,项⽬中推荐这种⽅式。
using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。
展开命名空间中全部成员,项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便推荐使⽤。

第一个指定命名空间介绍过了,每次去指定命名空间会很麻烦,所以我们可以完全将命名空间全部展开,但这样也会有很大的风险上面也介绍过了:
利用using namespace +域的名称这个方式就不用每次都去指定命名空间,但这个方式在写项目的时候不推荐。
如果我们频繁使用域中的某个成员,我们也可以只展开某个成员变量即可:
这样就是只展开某个成员变量的方法。
在现实过程中前两种方法结合使用,第三种不建议

三.C++输入&输出:

<iostream> 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输 出对象。
std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输 ⼊流。
std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流
std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。
<<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)
使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊ 输出可以⾃动识别变量类型(本质是通过函数重载实现的,这个以后会讲到),其实最重要的是
C++的流能更好的⽀持⾃定义类型对象的输入输出
利用C++输入输出的最大的好处就是自动识别类型,不需要像C++那样整形用%d 字符型用%c什么的,所以很方便:
我们每次在写C++时都要包括头文件和std的命名空间展开。
输入输出可以一行输入输出多个。
输出是这样所以输入也可以自动识别:
上面有小数,那就有个疑问就是如何限定小数点后保留几位,在C语言中很简单就是(小数点+保留几位小数)即可,C++也可以但是很麻烦不建议,因为C++包含C语言。

四.缺省参数:

缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参 则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有些地⽅把缺省参数也叫默认参数)
全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。
函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省
值。
什么是缺省参数呢,在红框框里的函数参数可以看见,赋给了变量a一个初始值0,如果在调用函数时没有给初始值,这时用的就是默认给的初始值0,此时这个初始值0就叫缺省值,如果调用了函数并给了初始值那就用这个初始值。
什么是半缺省和全缺省呢:
全缺省就是函数里的所以参数全都给了初始值,而半缺省就是函数里的参数并没有完全给定初始值。
C++规定半缺省时必须从右往左,并且中间不能有间隔
下面是几个错误的样例:
这个就属于中间有间隔了,所以是错的
这个就属于从左往右了,也是错误的。
说完了给值,这里说一下怎么调用全缺省和半缺省的函数:
首先说一下全缺省的四种调用方式:
第一种就是不给初值,直接用缺省参数。 第二种就是只给一个初始值,函数的调用,缺省值给的顺序必须是从左到右并且不能跳跃给值,所以下面两种就不用分析了就是给值给了几个。
下面分析一下半缺省的三种调用方式:
半缺省三种调用方式,半缺省的缺省值是从右往左连续缺省,所以第一种就是给第一个变量的值,下面两种方式同理
看一下运行结果:
没给值就要缺省值,给值的就用给的变量值。

缺省参数的实际意义:

                                       

在栈中插入数据时,如果空间不够我们就需要来扩容,但此时扩容就得2倍2倍的扩,如果我们一开始就要放入1000个数据,我们就可以利用一个缺省参数,直接改参数为1000.:

如果不知道就给缺省值4,就很方便,在初始化栈的时候就把空间开辟完成了,下面就不需要去扩容了,就会大大提高效率。C语言就没有缺省参数每次加的时候都会去扩容就会减缓效率。

• 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省
值。
就接着上面演示如果要存1000个数据,我们就只能在声明里给缺省参数,不能在函数定义里给缺省参数:
如果去运行就会报错重定义了,所以缺省参数不能在声明与定义中同时出现。
因为如果同时出现,如果你把数写的不一致,编译器就不知道到底要用哪个数去运行。

五.函数重载:

C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者 类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。
这就是函数重载的好处,虽然函数名相同但是参数类型不同,所以可以调用,这在C语言当中是会报错,编译不会通过的,这就是函数重载的好处。能很明显看出这时加法,就不用再像C语言那样去再重新定义另一个名称的函数。
像这两个函数是因为参数个数不一样构成函数重载但在调用时就会出问题,因为我不知道要调用哪一个函数。
但如果半缺省就可以运行成功:
第三种就是参数顺序不同构成的函数重载。
返回类型不同不构成重载函数。

六.引用:

引用可以让C语言的指针更好利用。

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间
在语法层引用是不会额外开空间的,跟变量共用一个空间。
类型& 引⽤别名 = 引⽤对象;
这四个变量名虽然不一样,但是地址是一样的因为只是给a取了别名罢了,就是同一个东西罢了,用的还是也同一块地址。
这里来调试一下,虽然只是给d++了但其实都是一个东西,所以都加加了。

     1.引用的使用:

    引⽤在实践中主要是于引⽤传参引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象。
     引用做返回值后面再介绍,这里就介绍一下引用传参的实践

     

这里就不用传地址了,rx就是x的别名,rx就相当于x,同理ry就相当于y,所以在函数里面的操作就算不传址也能改变x和y,rx和ry的改变就是x和y的改变。

这里改变的是变量的值就引用变量,如果我们想改变指针,就引用指针:

这里的引用就是引用的指针,改变了我们一开始用C语言写用二级指针去完成就很麻烦

单链表的实现:单链表专题-CSDN博客

    2.引用的特性: 

          • 引⽤在定义时必须初始化

          

          • ⼀个变量可以有多个引⽤

          

          • 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
这里就不是引用,仅仅只是赋值,并且d的地址与其不一样,因为引用一个实体后,再不能引用其他实体。

3.const引用:

可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访 问权限在引⽤过程中可以缩⼩,但是不能放⼤。(单纯的拷贝不存在权限的放大和缩小)
不需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场景下a*3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是时,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。
所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,
C++中把这个未命名对象叫做临时对象
• 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤:
这个就属于权限放大了,因为原变量是cosnt变量,是不可以修改其数值的,但是现在引用却不是const变量了,导致现在权限被放大了所以这是会报错的。
这样就可以了,这就是权限的平移。
当权限缩小也可以运行,不会报错:
这里缩小的只是引用的rb,并没有影响b本身的权限,简单来说,b可以++,rb不可以++:
还有值得注意的是,cosnt可以给常量引用,而普通的引用不能引用常量:
运算符的引用也需要用const去引用,因为运算符运算结束是会产生临时变量,而临时变量具有常量型,所谓常量就无法改值,所以就不能有权限的放大和缩小,所以要用const去引用:
在这一步d给i发生了类型转换 也是隐式变量存储,会产生临时对象,只读不能写,所以要引用的话还必须是const引用, 所以这里引用的并不是d这个变量本身而是引用的d变成int类型的临时对象
               
所以cosnt引用既能引用常量也能引用const类型的常量,也能引用临时对象。也就是说const引用的范围十分广泛。所以cosnt引用传参就非常有用,后面就会有印证。

   4.指针和引用的关系:         

C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。
语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。
sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)
指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

我们想要更直观看出指针和引用的区别我们就可以从汇编指令来看:

            

 

可以看到汇编指令上底层上引用和指针引一样的。

引用是可以空引用的:

七.inline:

⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就 可以提高效率
C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调
试,C++设计了inline⽬的就是替代C的宏函数。
inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。
• C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调
试,C++设计了inline⽬的就是替代C的宏函数:
现在我们就来回顾一下C语言的宏函数:
这三种宏函数的写法全是错误的, 正确写法如下:
           
里面外面都要加上括号。
宏函数展开是什么样的呢:
然而这里就产生了三个问题:
       
// 为什么不能加分号?
这里我列举一种情况就知道为什么不能加分号了
如果在这里你加了分号就会报错,就会提前结束语句,这都是坑,所以这里就能很直观看出为什么不加分号

                           
 // 为什么要加外面的括号?

也是来看一个样例:

这里不加括号就会先算2*5+1了,所以要加上括号才对。


 // 为什么要加里面的括号?  

还是来看一个样例:

我们知道与和或的优先级很低,所以这里我们是先想完成与和或,但是如果不加括号的话就先完成了x+y,所以要加上里面的括号。

所以总的来说宏函数十分麻烦,有很多注意事项,所以这里C++引入inline函数:

就是在要写的函数名前面加上inline

我们还是来看看反汇编码:

这里call就代表不会展开,内敛函数默认就不会展开。

inline之所以会提高效率,如果我有100条指令,而有1000个地方调用,如果每次调用都展开的话是1000*100条指令,不展开的话就是1000+10条指令,所以inline不展开就会提高很高的效率。

inline不建议 声明和定义分离到两个⽂件 ,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错:
         
这样写就会报错,所以inline的声明和定义要在一个文件当中才行。内敛直接放到(.h) 头文件中即可.

八.nullptr

NULL实际是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码:
                 
C++中NULL可能被定义为字⾯常量0,或者C中被定义为⽆类型指针(void*)的常量。不论采取何种定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦,本想通过f(NULL)调⽤指针版本的
f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。f((void*)NULL);
调⽤会报错。
我们原以为第一次调用调用的是第一个f函数,第二次调用调用的是第二个f函数,其实调用的都是第一次,这时因为在C++中NULL被宏替换成了0,这里就出现了歧义 。
所以要写出nullptr才行,这样就不会产生歧义。
这就是开始学习C++之前的准备知识,接下来就会正式开启学习C++

http://www.kler.cn/news/294528.html

相关文章:

  • Mail PHP: 如何设置SMTP服务器以发送邮件?
  • Vue 3结合Element Plus中,实现一个级联选择器(Cascader)来展示省市区
  • CSS解析:定位和层叠上下文
  • Elasticsearch 向量数据库本地部署 及操作方法
  • Learn ComputeShader 09 Night version lenses
  • 如何使用Prometheus与Grafana监控Kubernetes集群
  • 图论基础1
  • 重启顺风车的背后,是高德难掩的“野心”
  • 高分辨率音频和传统音频区别
  • 学习笔记--Docker
  • 【机器学习】朴素贝叶斯网络的基本概念以及朴素贝叶斯网络在python中的实例
  • 【SpringBoot】使用Nacos服务注册发现与配置管理
  • 主板选购2
  • 【C/C++】Linux\Windows为什么频繁使用size_t
  • 服务器蓝屏该怎么办
  • Vue3 父子传参 简单易懂
  • Mybatis Plus快速重构真批量sql入库操作
  • PLC+AIoTedge边缘物联网平台能否替代 PLC+Wincc?
  • 13. 说说 MyBatis 的缓存机制?
  • MySQL 数据库管理与操作指南
  • 自定义view中常用到哪些方法作用分别是什么
  • 专栏前言-WooYun漏洞库环境搭建
  • Java详解String 字符串类以及String内存原理、StringBuilder类、StringJoiner类(附有代码+案例)
  • MASt3R:从3D的角度来实现图像匹配(更新中)
  • 前端工程化2:从0-1的eslint插件开发教程
  • 基于Prometheus 和K8S kubernetes 构建 搭建监控告警系统
  • 单点登录:cas单点登录实现原理浅析
  • 【Vue】状态管理模式Vuex
  • 通信工程学习:什么是AM标准调幅
  • [情商-13]:语言的艺术:何为真实和真相,所谓真相,就是别人想让你知道的真相!洞察谎言与真相!