1.命名空间
2.c++输入&&输出
3.函数重载
4.缺省参数
5.引用
6.内联函数
7.auto关键字(c++11)
8.基于范围的for循环(c++11)
9.指针空值——nullptr(c++11)
1.命名空间
在c++中,变量,函数,类将在全局域中大量存在,而这稍不注意就会引起命名冲突和名字污染的问题,c++用namespace关键字解决了这类问题。
下图就是命名冲突了,rand是一个函数,将这个名字用于定义变量就会产生冲突。
1.1命名空间定义
定义命名空间,需使用namespace关键字,后面更着的是命名空间的名字(自定义的),然后接{},花括号里写命名空间的成员(如变量,函数,类,甚至可以嵌套命名空间)。
现在用命名空间解决一下上面的问题:
原理:一个命名空间就定义了一个作用域,叫做命名空间域,命名空间中的内容都局限与命名空间中,与命名空间外的内容互不影响。
1.2命名空间的使用
1.2.1作用域限定符
1.2.2使用using引入成员
1.2.3使用using namespace引入整个命名空间
1.3细节
1.3.1命名空间的合并
在同一个项目的不同源文件中名字相同的命名空间会自动合并。
1.3.2默认访问优先级
默认优先访问所在局部域的内容,找不到的话,再去全局域以及命名空间中已经引用的部分(这两者的优先级是同级的)。
由于命名空间已引用的部分和全局域的部分优先级相等,名字相同会发生歧义
这并不是编译错误,而是访问时有歧义,如果不访问是不会报错的。、
而且,如果使用作用域限定符指定访问,就不会有歧义了,所以可以运行。
2.c++输入&&输出
2.1 hello world
2.1.1说明
1.cout是一种类,叫做标准输出类,作用类似与printf,cin也是一种类,叫做标准输入类,作用类似于scanf,它们二者都在头文件<iostream>中。
2.endl是特殊的c++符号,表示换行,它也在头文件<iostream>中。
3.cout,cin,endl都在命名空间std中,所以有了using namespace std;
4.<<是流插入运算符,>>是流提取运算符。
5.使用cout和cin输入输出不需要考虑格式的问题,会自动识别。
6.相信大家都会使用cin和cout,这里就省略了。
7.命名空间中除了cin,cout,endl以外,还有很多其他的元素,如果只使用输入输出,最好只展开一部分(如下图)
我们平时练习时是可以完全展开std命名空间的,但是做项目时最好用上图的做法。
3.函数重载
3.1函数重载的概念
函数重载时函数的一种特殊情况,c++允许同一作用域中声明几个功能类型的同名函数,这些同名函数的形参列表不同(参数个数不同或类型不同或类型顺序不同)。
3.2重载函数的调用
根据实参的类型找到对应的函数:
那么,如果实参类型找不到对应的函数该怎么办呢?还能像之前的函数一样,发生隐式类型转换吗?
看是否存在歧义:如果使用隐式类型转换后有多个函数符合条件,如下图,则不行!(因为实参即可以向第一个函数隐式类型转换,也可以向第二个函数隐式类型转换)
但是,如果不存在歧义,则是可以的:
这里的实参只能向第二个函数隐式类型转换,通过隐式类型转换无法调用第一个函数,那么这时候是可以用隐式类型转换的。
4.缺省参数
4.1缺省参数的概念
缺省参数是声明或定义函数时为函数形参指定缺省值,在调用函数时,如果没有指定实参,将采用该形参的缺省值。
4.2语法规则
1.缺省值必须从右向左依次给出,不能隔着给。
2.缺省参数不能在函数声明和函数定义中同时出现。
3.缺省值必须是常量或全局变量。
4.3含缺省值的函数重载
形参参数个数不同,构成函数重载,不报错。
但是调用就不可以了,有歧义,该情况编译器不知道调用哪个重载函数!
但只要没有歧义(传了一个实参,第二个函数形参不符,只能调用第一个),就可以调用了。
4.4全缺省函数和半缺省函数
缺省函数:含缺省参数的函数叫缺省函数
全缺省函数:该函数所有的形参都有缺省值
半缺省函数: 这里不能顾名思义,并不是函数一半的形参有缺省值,一半的没有。半缺省函数的形参有一部分是缺省值,如果函数只有一个形参有缺省值,那它也是半缺省函数;如果一个函数有10个形参,那么1个形参有缺省值,2个形参有缺省值,3个形参有缺省值……8个形参有缺省值,9个形参有缺省值,这9种情况下,该函数都算是半缺省函数
5.引用
5.1引用概念
引用就是给已定义的变量(包括对象,结构体变量,甚至包括函数指针变量)取别名,从语法的角度看,编译器不会开一块新的空间,而是和引用的对象共用同一块空间。
类型(一般就是引用的对象的类型)& 别名 = 引用的对象的名字;
a是引用的对象,a1是为a所取的别名。
a1是a的别名,编译器没开一块新的空间(地址相同),它们共用同一块空间。
5.2引用特性
1.引用必须在定义时初始化
2.一个变量可以多个引用(别名)
3.如果已经给一个变量取了别名,那这个名字就不能在给其他变量别名时使用
5.3常引用和权限问题
0是常量,具有常性(不可以更改),这里的a没有常性,可以更改,出现了权限放大,所以报错。
但只要用const限制a就可以运行了。
与上面同理,这也是权限放大,解决方法一样,使用const限制a1就可以了
即然权限不能放大,那权限可以缩小吗?
答案是可以的!
补充:在类型转换以及运算时都会产生临时变量,临时变量具有常性。
a1实质上是对类型转换时所产生的临时变量进行引用。
避免了权限放大,这种写法也是合法的!
c实质上是对运算所产生的临时变量进行引用。
5.4使用场景
5.4.1做参数
a是x的别名,b是y的别名,交换a和b的实质就是交换x和y。
5.4.2做返回值
b是a的别名,可以通过b访问到a
6.内联函数
6.1概念
用inline修饰的函数叫内联函数,编译时直接在调用处展开,不会建立栈帧,可以提高运行效率。
6.2特性
1.inline是一种空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。
2.缺陷:可能使文件变得很大。
3.优势:少了调用开销,提高了运行效率。
4.inline只是给编译器的一个建议,编译器有权利拒绝。原因:如果内联函数的函数体的代码量巨大,内敛付出的代价是很可怕的(让文件变得超级大)。
5.inline不建议声明和定义分开,inline被展开后,就没有函数地址了,链接就找不到了,从而产生链接错误。
7.auto关键字(c++11)
随着学习的深入,类型的名字越来越长,越来越复杂,容易写错,有没有什么办法解决呢?
答案是有的,auto可以自动识别类型。
对比两段代码,auto成功减小了代码量,也降低了复杂程度。
7.1使用细则
1.使用auto定义变量必须在定义时初始化,因为在编译阶段要根据初始化表达式来推导auto的实际类型。所以,auto是一个类型声明的“占位符”,编译器在编译期会将auto替换为变量的实际类型。
2.用auto声明指针类型时,写auto和auto*没有任何区别,但是auto声明引用类型时必须加&。
3.适用auto在同一行声明多个变量时,它们的类型必须相同,因为编译器实际只对第一个变量的类型进行推导,然后用推导出来的类型定义其他变量。
7.2auto不能推导的场景
1.auto不能作函数参数
2.auto不能直接用来声明数组
8.基于范围的for循环(c++11)
8.1范围for的语法
for循环后的括号由冒号分为两部分:第一部分时范围内用于迭代的变量,第二部分则表示被迭代的范围。
for循环会将arr中的值循环地依次放入用于迭代的变量e中(这里也可以写a,b,c之类的,不是必须写e)每放一次进一次循环,而这个程序就是顺序遍历数组了。
9.指针空值——nullptr(c++11)
9.1c++98中的指针空值
我们之前学过的指针空值都是用NULL来表示的,但是,NULL不一定被定义成指针空值((void*)0),可能被定义为字面常量0,现在我们来验证一下。
我们本来是想用指针空值的,但这里编译器明显将NULL识别成了字面常量0
NULL默认情况下被看作整型常量,如果想在这种情况下以指针方式来使用,必须对其强制类型转换。
9.2c++11中的指针空值
c++11中引入了关键字nullptr来表示指针空值,它不会表示字面常量0,就不有上述问题了(如下图)
1.使用nullptr不需要包含头文件,nullptr是一个关键字。
2.在c++11中nullptr和((void*)0)所占字节数相同
3.为了提高代码健壮性,表示指针空值时建议使用nullptr。