C++前言:
C++ 的发展历史可以追溯到1979年,当时C语言以其效率和灵活性成为广泛使用的系统编程语言,但它也有一些限制,例如缺乏直接支持面向对象编程(OOP)的特性。
之后Bjarne Stroustrup(也就是C++之父)是C++的创始人和主要设计者。他在1980年代初期在贝尔实验室工作时,开始思考如何扩展C语言,使其能够更好地支持OOP和其他现代编程方法。他最初称这个扩展为"C with Classes"(带类的C语言),这是一个在C语言基础上添加了类、继承、成员函数等概念的版本。
1983年,Bjarne Stroustrup正式将其改进的语言命名为C++,这个名称代表"增强的C"(C增强版)。C++在语法和语义上保留了C语言的大部分特性,同时引入了面向对象编程的特性,如类、封装、继承和多态性,使得程序员能够更方便地进行复杂系统的设计和实现。
第一个C++语言:
因为C++是在c的基础上延伸出来的语言,所以C++兼容c的大部分语法,也可以在c++的编辑器上编辑c语言的代码。
由上图可以看出文件后缀是以.cpp结尾表示这是一个C++编程语言源代码文件,而任然可以编写C语言的代码。
C++有⼀套⾃⼰的输⼊输出,严格说C++版本的hello world其实是这样写的,这里代码看不懂没关系,后续小编会进行讲解。
C++的命名空间
1.namespace(命名空间)的概念
变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
namespace的定义:
定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
如上图就定义了一个名为cat的命名空间,同学们也可以用自己名字或者其他的名字进行命名,而在命名空间里存放着类型为int的rand变量。
namespace只能定义在全局,不能再局部进行定义。
namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,这个小编之后会进行介绍。
为了观察namespace的使用,小编以下是用C写的一段代码:
上图代码,小编创建了一个int类型的全局变量rand,并在主函数里尝试输出rand,此时可以看出函数在编译的时候就进行了报错,给出的理由是rand重定义,表明rand在这里是一个函数,而自己又给rand定义成了一个int类型的全局变量,那么在c++的编译是时候是不会通过的。那么如果就想用rand名字来创建变量应该怎么做呢?
从上图可以看到,在使用namespace里进行定义rand并再主函数里成功的输出了它,此时的变量rand与函数rand就进行了隔离,输出时输出的是命名空间里rand变量。
关于局部域、全局域以及命名空间域
从namespace的定义中可以得知,namespace属于独立的一个域 ,与局部域,全局域都不相同。
在上图代码中,创建了三个类型相同且变量名相同的变量,而在编译的时候没用发生报错,这是因为三个变量都处在不同的域当中。
命名空间域就类似于一堵墙,它把变量a进行了隔离名字冲突就解决了,只有申请访问的时候才会查找到它。
域作用限定符
': :':表示域作用限定符,它的作用是指示在命名空间、类、结构体或枚举类型中的特定成员的访问。
第一句 printf里并没有加': :'符号,表示在局部域main函数里查找的变量。
第二句 printf里在': :'前面没加东西,表示是在全局域里查找的变量。
第三局 printf里再': :'前加了名为cat的命名空间,表示在命名空间cat里查找的变量。
命名空间的嵌套
在命名空间里同样也可以嵌套命名别的命名空间。假设张三和李四在同一个的项目里工作,那么如果张三和李四刚好在同一个命名空间里定义了相同的变量。
那么此时命名又冲突,总不能让张三和李四打一架来决定出谁才可以命名的变量。这时候命名嵌套就起到了作用。
此时编译也通过了,张三就用张三自己的命名空间,李四就用李四的命名空间,这样谁也不会起冲突。
多个同名的命名空间合并
在Stack.h文件里创建了名为cat的命名空间,空间里包含着栈的声明。
在Stack.cpp的文件里同样使用了名为cat的命名空间,并对栈的函数进行定义。
在主文件里,我们我们用cat命名空间里的栈类型申请了一个变量,并使用栈初始化函数进行初始化,可以看出程序编译成功说明了**在不同的文件中可以使用相同名字的命名空间,类似于将两个cat的命名空间进行了合并成为一个cat的命名空间。**这种不同于结构体,如果两个结构体使用相同的名字则会报错类型重定义。
在住文件里也同样定义了一个栈的声明并使用它,同样没有进行报错,说明了命名空间可以将变量进行很好的区分。
2.命名空间的使用
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。所以我们要使⽤命名空间中定义的变量/函数则有以下三种方式
指定命名空间
指定命名空间也就是编译器直接去指定的命名空间里找,不会再去别的地方搜索,可以有效的避免变量的混用,唯一的缺点就是每次使用都需要声明会略微麻烦。
使用using将某个命名空间展开
using主要用于声明命名空间、别名、模板类型和基类成员等方面。这里 using lisi::a;表示展开lisi命名空间里a变量。
但如果在展开的同时又在全局域里又声明了同样变量,编译器就会进行报错,说a重定义。原因是因为using的展开相当于在全剧域中暴露了命名空间里的变量,也就是说这两个a同时存在于全局域当中。
相当于给命名空间域开了一扇窗户,是的全局域里也能找到a。
展开全部命名空间
代码using namespace lisi;表示将lisi空间的内容全部展开到全局域中,不推荐使用冲突⻛险很⼤。
C++的输入输出IO流:
头文件<iostream> 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象。
std::cin是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输⼊流,类似于C语言的scanf函数。
std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流,类似C语言的printf函数。
std::endl是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区,相当于C语言的换行符'\n';
<< 是流插⼊运算符,**>>**是流提取运算符。
cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使⽤⽅式去⽤他们。
由上图代码可见,定义了三个类型不同的变量,并使用cin进行输入,可以看到cin能够智能识别变量类型,并不需要再去像C语言那样手动确定变量类型的输入模式。接着通过cout进行输出,并且每一小段输出内容后都跟了endl进行换行刷新。
C++缺省参数
缺省参数的概念:
在C++中,缺省参数(默认参数)是指在定义函数时为参数提供一个默认值。这个默认值可以在调用函数时省略相应的参数,如果不给此函数进行传参则编译器会调用默认值。这种功能使得函数在不同的调用情境中可以有不同的行为,同时简化了函数的调用方式。缺省参数同时又分为全缺省与半缺省。而在C语言中是不支持缺省参数。
全缺省与半缺省
全缺省:
全缺省就表示,在使用函数的时候全部不进行传参,使用的都是函数的默认值(缺省值)。如上图,在函数Add中定义了三个形参a,b,c。并且都给形参进行了赋值操作。而在主函数进行Add的调用时并没有进行传参的操作,而是直接使用并接受了Add函数的返回值,可以看到sum值最终接受的参数就是a,b,c的默认值进行相加返回的值。
半缺省:
半缺省就表示只给部分形参赋缺省值,如上图代码我只给形参c赋了缺省值,而a,b使用了指定参数进行传参。
带缺省参数的调用的注意事项
1.带缺省参数的函数调⽤,C++规定缺省必须从右往左缺省,传参必须从左到右依次给实参,不能跳跃给实参。
如上图,c没有进行缺省,传参只传了一个实参,那么这个实参将会赋值给a,而c并被赋值到,编译进行了报错。
2.缺省参数的声明和定义不能同时给,并且也不能只在定义的时候给。
如上图,分别在函数声明与定义的两个文件里都将形参n进行了缺省,编译器在编译时也进行了报错说重定义参数。
如果只在定义的时候给参数进行缺省而声明的时候不给缺省,那么编译也有可能不会过。
C++函数重载
函数重载的概念:
C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同,通俗的来说,函数重载就类似于,有很多个同名同姓的人,但每个人都是不同的个体。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。
函数重载的分类
参数类型顺序不同:
上图定义了两个同样的Add名称的函数,而两个函数的参数的类型顺序不同那么这两个函数就支持函数重载。
参数类型不同:
上图定义了两个同样的Add名称的函数,而两个函数的参数类型不同那么这两个函数同样支持函数重载。
参数个数不同:
上图定义了两个同样的Add名称的函数,而两个函数里的参数虽然相同但参数的个数不同那么这两个函数同样支持函数重载。
函数的重载与函数的返回类型无关:
当两个相同命名、相同参数的函数只是返回类型不同时进行编译,程序进行了报错,说明只有返回类型不同的函数是不支持函数重载的。