目录
一、命名空间
1.命名空间的定义
2.命名空间的使用
二、输入和输出
三、缺省参数
1.缺省参数的概念
2.缺省参数的分类
1)全缺省参数
2)半缺省参数
四、函数重载
五、引用
1.引用的概念
2.引用的特性
3.引用和指针的区别
六、内联函数
七、 auto关键字
八、基于范围的for循环
一、命名空间
当多个程序员合作完成同一个项目时,可能会发生标识符之间的命名冲突问题,例如程序员A给一个函数取名为fun,而程序员B也给另一个函数起名为fun,这时候就会引起冲突,而c++中的命名空间就很好地解决了这个问题。使用命名空间(namespace)的目的就是对标识符的名称进行本地化,以避免命名冲突或名字污染。
1.命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员,可以是变量、函数甚至结构或类等标识符。
当我们要使用空间里的变量或者函数等时,我们需要借助作用域运限定符(::)来访问。需要注意的是:命名空间的定义体不需要以分号结束
接下来我们看个例子:
代码中的cout与C语言中printf一样,有着打印的作用,这个在后边讲解,我们可以从运行结果看到,有了命名空间,在命名空间里的变量名可以与外边的相同,我们可以理解为命名空间将它里面的变量等保护起来了,当我们要用这些变量时需要交代命名空间的名字,这样编译器才能找到这个变量。
三点说明:
1、命名空间可以嵌套,即在一个命名空间中可以定义另外一个命名空间
2、同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成在同一个命名空间中
3、一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
2.命名空间的使用
我们知道,编译默认查找变量的顺序是:当前局部域-->全局域-->展开的命名空间,所以要使用命名空间中的变量时需要展开或者做出说明。
①加命名空间名称及作用域限定符(这个在上边已经提到了)
以上面的例子为例,我们要用到命名空间Tom中的变量啊,就需要在a前面加上Tom::。
②使用using将命名空间中某个成员引入
可以根据我们的需要展开某一个,例如:我们在main函数前边加上using Tom::f,在后边使用f时就不必加上Tom::了。
③ 使用using namespace 命名空间名称引入
这就相当于将命名空间全部展开了,我们可以直接用命名空间里的变量、函数等,但是这样并不是特别好,如果有多个命名空间,并且它们中有多个相同的变量名,这时将它们都展开就会带来命名冲突问题。
二、输入和输出
在C++中,用cin来输入,cout来输出,当然,C++兼容C,用scanf和printf来输入输出也是可以运行的。
注意:
1.使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,需要包含头文件<iostream>
2.使用cin要与>>(流提取运算符)一起,使用cout要与<<(流插入运算符)一起
3.使用cout和cin时不需要像printf和scanf那样控制格式,因为cout和cin可以自动识别变量类型
4.C语言中我们用'\n'来换行,但是在C++中可以使用endl来换行
5.cout、cin、endl位于命名空间std中,所以使用时要么在前边加上std::,要么在main函数前边加上using namespace std; (通常不推荐,但是在平时练习代码时由于代码量少,是可以展开的),如果使用某个比较多(通常使用cout较多),就可以加上using std:: cout; 。
三、缺省参数
1.缺省参数的概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参。
这种机制可以简化函数的调用过程,避免繁琐的参数传递,同时提高函数的灵活性。具体来说,如果调用函数时提供了实参,那么函数将使用传入的实参值;如果没有提供实参,则使用缺省值。
2.缺省参数的分类
1)全缺省参数
一个函数可以有多个参数,在定义函数时给所有参数都加上对应的值就是全缺省。
2)半缺省参数
类似的,在函数定义时不全给参数对应的值就是半缺省参数。
注意:
1.半缺省参数必须从右至左给出,不能从左至右,也不能跳着给出
2.缺省参数不可以在定义和声明时同时出现,缺省值只能定义一次,即要么在函数原型说明时定义,要么在函数定义时定义,但不能两者都定义
3.传实参时是从左至右给,不能跳过中用空格代替
4.缺省值必须是常量或者全局变量
四、函数重载
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这
些同名函数的形参列表(参数个数或类型或类型顺序)不同,常用来处理实现功能类似数据类型
不同的问题。
我们在判断几个函数是否构成函数重载也是从上面加红加粗部分的几个点来入手的,即是否在同一作用域、函数名是否相同、形参列表是否不同,只要满足这几个条件便构成函数重载。
几个注意点:
1.返回值不能作为判断是否构成函数重载的条件2.不同命名空间里的同名函数不构成函数重载,因为是不同作用域
3.当有函数fun()和函数fun(int x = 2)即含有缺省参数时,如果调用函数并且不给定参数时,会造成错误,因为编译器不知道调用哪一个
一道面试题:为什么C语言不支持函数重载而C++支持呢?
答案是: 因为链接时C语言是用函数名去查找,而C++是用修饰后的函数名来查找的。
五、引用
1.引用的概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。
引用声明的一般方法:类型& 引用变量名(对象名) = 引用实体。
引用其实与指针非常相似,但是又有所区别,接下来我们看代码来具体了解其用法:
从运行结果可以看到,b的值和地址与a完全相同,b就是a的别名,c 的值与a相同,但是地址不同,c是指向a的指针,它有自己的地址。我们可以看到,当对b修改时,a的值也会改变,因为本质上a和b是完全一样的。
我们知道,在C语言中要实现两个数的交换,就必须传地址来交换,而有了引用就方便了许多;在函数传值时,我们知道形参是实参的临时拷贝,有了引用,就不必去拷贝,直接可修改,更高效。
2.引用的特性
1.引用在定义时必须初始化
2.一个变量可以有多个引用
3.引用一旦引用一个实体,就不能再引用其他实体
我们来看一个例子来解释第三点:
从这个例子可以看到,x、a、b均变成了2,这也正是因为引用只能有一个实体,在语句“x = b;”中由于x是a的别称,实际上就是将a赋值为2 。
4.引用的权限可以平移和缩小,但是不能放大
#include <iostream>
int main()
{
int a = 1;
const int b = 2;
int& x = a; //权限的平移
const int& z = a; //权限缩小
//权限不能放大
//int& y = b; //error
const int& y = b;
return 0;
}
5.类型转换、表达式运算会生成临时变量,引用时必须用const修饰
不论是隐式类型转换还是显式类型转换,抑或涉及到表达式运算,都会生成临时变量,不能将一个临时表达式的值绑定到非const引用上,因为临时表达式的生命周期仅在表达式求值期间有效。
3.引用和指针的区别
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
4. 没有NULL引用,但有NULL指针
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(将在下面通过代码进行演示)
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用是编译器自己处理
9. 引用比指针使用起来相对更安全
关于第5点,我们来看代码说明:
从上面这个示例可以看到,不论是什么类型的指针,用sizeof求其大小,都是地址空间所占字节个数。而对于引用来说,就是引用类型的大小,在上面中char是一个字节,int是四个字节,所以最后得到的字节数就是1和4。
六、内联函数
内联函数的提出是为了提高某些调用频繁、代码体积较小的函数的调用效率。
内联函数声明的语法是把关键字inline放在函数原型的前面。
说明:
1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到。
七、 auto关键字
auto是一个类型推导关键字,它告诉编译器自动为变量选择正确的类型。
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编
译期会将auto替换为变量实际的类型。
注意点:
1.用auto声明指针类型时,auto与aoto*没有区别,但是声明引用类型时必须加&
2.在一行定义多个变量时,这些变量的类型必须一致
3.auto不能作为函数参数,因为建立栈帧时需要知道对应大小
4.auto不能直接用来声明数组
示例:
#include <iostream>
int main()
{
int a = 1;
auto& x = a;
auto p = &a; //声明的对象可以是指针,也可以是普通变量
//auto* pp = a; //error
auto* pp = &a; //使用auto*时,声明的对象必须是指针
}
八、基于范围的for循环
基于范围的for循环(Range-based for loop)是C++11引入的一种新特性,它提供了一种更简洁、更直观的方式来遍历容器(如数组、向量、列表等)或其他可迭代对象中的元素。基于范围的for循环自动处理迭代器的创建和管理,使得代码更加清晰和易于理解。
for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
示例:
这与传统的for循环有很大不同,例如上述例子,for循环括号里面是变量类型加变量名(自己取),再加冒号(:),最后加上数组名,这样就可以遍历数组了,并且将数组元素打印,需要注意的是,只能顺序遍历数组,不能逆序。