【C++】命名空间、缺省参数、函数重载、引用
文章目录
- 1.认识命名空间
- 2.命名空间的使用
- 3.C++的输入和输出
- 4.缺省参数
- 4.1缺省参数的概念
- 4.2缺省参数的分类
- 5.函数重载
- 6.引用
- 6.1引用的概念
- 6.2引用的特性
- 6.3常引用(重点题目)
- 6.4引用和指针的区别
1.认识命名空间
下面让我们学习一下第一个关键字namespace
首先,我们来看下面的代码
我们定义了一个名为 rand 的全局变量,此时可以正确的打印出来。
当我们引用了这个头文件时,我们发现此时代码就会报错,如下图。
我们都知道,rand是一个产生随机数的函数名,此时该名称与我们定义的全局变量命名发生了冲突,编译器就不知道选哪个了。
然而,C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
命名空间有几种使用方式:
- 正常的命名空间定义
//aim 是命名空间的名字,实际中一般以项目名称命名 namespace aim1 { int rand = 10; }
- 嵌套定义
namespace aim1 //命名空间1 { int rand = 10; namespace aim2//命名空间2 { int rand = 20; } }
- 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
两个文件中的同名命名空间最后会进行合并,因此会出现重定义问题。
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
2.命名空间的使用
使用命名空间解决访问冲突的问题后,我们发现仍然访问不到它,此时rand使用的依然是rand函数,打印出来函数的地址。
下面就来介绍一下命名空间是如何访问的,有3种方式:
- 加命名空间名称及作用域限定符
作用域限制符就是两个冒号,此时我们就可以访问到定义的变量了
- 使用using将命名空间中某个成员引入
using是我们学习的第二个关键字
我们发现,只有被引入的成员才可以被访问,其它成员依旧不可以。若想访问其它成员,也需要将其引入
- 使用using namespace 命名空间名称引入
此时我们发现,a变量可以访问了,然而rand又出现了问题。什么原因呢?
我们知道,编译器的默认查找是:
a. 当前局部域
b. 全局域
b. 到展开的命名空间中查找
此时我们的全局域中和命名空间中都有rand,显然不知道选哪一个,此时我们就需要指定了。因此使用第三种方式时要慎重,不然就是自找麻烦
此时我们就可以发现,若某一成员需要经常使用,可以将其引入;对于不常使用的成员,可以使用命名空间加作用域限定符。
很显然,引入了成员后使用更加方便。
自己在学习中遇到的问题:为什么使用命名空间中的宏时不需要指定?
宏定义是在编译时预处理阶段完成的,而命名空间是在编译时进行名称空间分配和解析的。在预处理阶段,编译器并不知道命名空间的存在,所以它无法对命名空间进行解析,因此也就无法指定命名空间。
当使用命名空间内的宏时,编译器会在预处理阶段将宏展开成相应的代码,而展开后的代码中不包含命名空间,因此不需要指定命名空间。
3.C++的输入和输出
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含头文件以及按命名空间使用方法使用std
- cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含头文件中。
- 是流提取运算符。
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型
浮点数输出时,也不会像scanf 那样默认小数点后6位;它是原样输出。
- 输入时,也不需要取地址了
std命名空间的使用惯例:
std是C++标准库的命名空间,如何展开std使用更合理呢?
- 在日常练习中,建议直接using namespace std即可,这样就很方便。
- using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以在项目开发中建议使用像 std::cout这样使用时指定命名空间 或 using std::cout展开常用的库对象/类型等方式。
4.缺省参数
4.1缺省参数的概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
4.2缺省参数的分类
- 全缺省参数
2. 半缺省参数
注意:
- 半缺省参数必须从右往左依次来给出,不能间隔着给。
- 缺省参数不能在函数声明和定义中同时出现
如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值
- 缺省值必须是常量或者全局变量
5.函数重载
C语言是不支持同名函数存在的,C++支持。
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数的个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
- 参数类型不同
- 参数类型的顺序不同
- 参数的个数不同
注意:
- 同一作用域中,同名函数要想同时存在,必须满足重载的规则。
- 不同作用域,可以有同名函数
- 返回值是不构成重载的(返回值不像参数,编译器对它是无法区分的,它也不是必须的)
- 重载的规则是针对于参数的类型和个数,与参数的名称无关、参数缺省无关
为什么C++支持重载,C语言不支持?
- 因为在程序进行链接的时候,是使用函数名去找地址的,如果声明和定义分离,C语言是直接使用函数名去找,区分不开。
- C++使用函数名修饰规则,可以区分。
6.引用
6.1引用的概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间,它们的地址是一样的。
定义方式:类型& 引用变量名(对象名) = 引用实体;
注意:引用类型必须和引用实体是同种类型的
引用就是祖师爷本贾尼觉得C语言指针不好才搞得,下面的代码就比指针方便的多。
6.2引用的特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
- 引用在定义时必须初始化
- 一个变量可以有多个引用
int main() { int x = 10; //一个变量多个引用 int& a = x; int& b = x; int& c = x; int& d = x; return 0; }
- 引用一旦引用一个实体,再不能引用其他实体
下面的代码中是将a变成y的别名?还是将y赋值给a呢?
很显然,它是将y赋值给了a
6.3常引用(重点题目)
void TestConstRef() { const int a = 10; //int& ra = a; // 该语句编译时会出错,a为常量 const int& ra = a; // int& b = 10; // 该语句编译时会出错,b为常量 const int& b = 10; double d = 12.34; //int& rd = d; // 该语句编译时会出错,类型不同,d为常量 const int& rd = d; }
- 权限的平移
int main() { const int m = 0; //权限的放大 //int& n = m; //error m的权限是可读,n的权限是可读可写的 //权限的平移 const int& n = m;//可以 //可以,不是权限的放大 //只是将m的值拷贝给x,x的修改不影响p int x = m; return 0; }
- 权限的缩小
- 权限的放大(错误、易错)
int main() { const int m = 0; //cosnt修饰指针,左定值,右定向 //p1可以修改,*p1不可以,const修饰的是*p1 const int* p1 = &m; p1++; //权限的放大,不可以 //int* p2 = p1; //易错 //权限平移,可以 const int* p2 = p1; return 0; }
总结:值之间没有权限的放大和缩小的概念,只是一个拷贝;指针与引用有权限放大与缩小的概念,权限只可以缩小,不可以放大
6.4引用和指针的区别
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
int main() { int x = 0; int& y = x; y = 10; int n = 10; int* p = &n; *p = 20; return 0; }
我们来看引用和指针的反汇编对比
引用和指针的不同
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
- 缺省值必须是常量或者全局变量
- 缺省参数不能在函数声明和定义中同时出现
- 输入时,也不需要取地址了