一、命名空间
1.C的命名缺陷
假设我们用c语言写了一段代码
#include<stdio.h> int rand = 0; int main() { printf("%d\n", rand); return 0; }
正常情况下,都会打印0
但是我们有时候会包stdlib这个头文件,我们发现报错了
根据上面的提示,我们不难发现,这是因为rand在stdlib这个头文件中已经被使用了。所以发生了重定义现象。
试想一下,一旦使用c语言去写一些大型项目,那么毫无疑问,会出现大量的重定义现象。
由此我们引入了namespace这个关键词,他的意思是命名空间
2.域和命名空间
我们在c语言的时候就了解过作用域的概念,比如下面的代码中,我们最后打印出来的结果是1,他是可以编译通过的。而打印出main函数里面定义的a的原因是因为局部优先。这个我们也称作局部域,而全局的a则位于全局域
#include<stdio.h> #include<stdlib.h> int a = 0; int main() { int a = 1; printf("%d\n", a); return 0; }
我们的域可以分为类域,命名空间域,全局域,局部域。
有局部域,优先访问局部域,没有局部,优先访问全局的。
当局部域和全局域都不存在的时候且命名空间域存在,直接输出a是无法输出的,也就是说无法直接访问命名空间域
上面的是局部域不存在的情况,我们现在来讨论一下如果全局域和局部域同时存在的话,如何访问全局域?
c语言是无法实现这个的。除非使用指针等。
而在c++中为了实现这个,引入了::操作符,这个操作符的作用是域作用限定符,他的使用如下代码所示,即在a的前面使用::,在左边可以加上一个空格(当然不加也无所谓),我们代表从全局域中去访问
#include<stdio.h> #include<stdlib.h> int a = 0; int main() { int a = 1; printf("%d\n", ::a); printf("%d\n", a); return 0; }
也就是说,通过这个操作符,我们可以实现无论是否有局部域,都可以去访问全局域
既然全局域的访问解决了,我们前面也提到过,命名空间域无法直接访问。那么该如何访问呢,答案是展开了命名空间域或者指定访问。
#include<stdio.h> #include<stdlib.h> int a = 0; namespace boundary { int a = 2; } int main() { int a = 1; printf("%d\n", ::a); printf("%d\n", a); printf("%d\n", boundary::a); return 0; }
如上所示是指定访问的方式
如下是展开命名空间的访问方式
那么如果即存在全局域又展开了命名空间域
则会出现不明确的现象
而如果不展开的话,那么就直接访问全局域
其实到了这里,我们也许已经混乱了。
但是其实核心如下:
1.优先局部域,其次全局域,最后访问展开的命名空间域或者指定的命名空间域
展开了命名空间域其实就相当于将这个变量给暴露在全局中,获得了全局的特点
2.如果是指定访问,那么没有特别需要注意的点,唯一需要注意的是,展开了命名空间域以后,他将获得全局的特点,如果使用全局的指定方式,那么将他可以访问这个变量
3.如果是直接访问,这个比较复杂
先看是否存在局部域,如果有局部域,那么直接访问局部域
如果没有局部域,那么就要再看是否存在全局域和命名空间域。
如果有全局域但没有命名空间域,那么直接访问全局域
如果没有全局域但存在命名空间域,如果没有展开,直接访问是无法访问到的,如果展开,直接访问可以访问到
如果全局域和命名空间域均存在,那么我们就要判断命名空间域是否展开
如果没有展开,访问全局域,如果展开,相当于两个相同命名的变量暴力在全局中产生冲突。
其次局部域不可以展开命名空间
3.命名空间的使用与嵌套
1.命名空间可以使用变量/函数/结构体
namespace N0 { // 命名空间中可以定义变量/函数/类型 int rand = 10; int Add(int left, int right) { return left + right; } struct Node { struct Node* next; int val; }; }
2.命名空间可以嵌套
//2. 命名空间可以嵌套 namespace N1 { int a; int b; int Add(int left, int right) { return left + right; } namespace N2 { int c; int d; int Sub(int left, int right) { return left - right; } } }
3.同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。 // ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个 namespace N1 { int Mul(int left, int right) { return left * right; } }
如上述的三种代码的嵌套等引用操作如下所示
int main() { printf("%d\n", N0::rand); printf("%d\n", N0::Add(2, 3)); printf("%d\n", N1::N2::Sub(1, 2)); return 0; }
二、输入输出
c++的输出是使用cout的
但是这个cout是定义在c++的标准库中的标准命名空间的,直接使用会报错的,我们想要使用这个输出,我们有两种方式可以做到
一种方式是直接展开命名空间,如下所示
另一种方式是使用指定访问
在使用第一种方式的时候,我们直接展开可能会展开冲突,这是很危险的。所以在项目中一般不直接展开
但是我们还有一种方式,就是将常用的给展开
我们现在来理解一下这个<<这个操作符,这个操作符在c语言中是移位操作符,在c++里面他又有了流插入运算符的含义
意思就是将"hello world "这个字符串插入到流中,而且我们还需要注意的是endl其实就是'\n',换行的意思,就是流插入hello world之后,在插入一个换行,将换行插入流
而且流插入可以使用多个类型
我们也可以发现cout的特点就是可以自动识别类型。
还有一个是输入cin,以及>>流提取运算符
他们的使用如下
#include<iostream> //using namespace std; using std::cout; using std::endl; using std::cin; int main() { int x = 10; double y = 11.11; cout << x << " " << y << endl; cin >> x >> y; cout << x << " " << y << endl; std::cout << "hello world" << std::endl; }
需要注意的是,使用cin也许会出现精度缺失的现象,我们有时候也需要打印宽度和精度的数据,这里我们建议采用c语言的方法,而不采用c++的方法,因为c++的方法过于繁琐
而且printf和scanf是比c++的cout和cin快的。
三、缺省参数(默认参数)
1.缺省参数的概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
例如下面的代码
#include<iostream> using namespace std; void Func(int a = 0) { cout << a << endl; } int main() { Func(); //没有传参时,使用参数的默认值 Func(10);//有传参时,使用指定的值 return 0; }
2.缺省参数分类
1>全缺省参数
如下代码所示,每一个参数都缺省了。这就是全缺省参数。在全缺省参数的传参中,我们如果需要指定值,必须要从左到右指定。中间不可以跳过一个传参。一旦出现一个缺省的,后面的必须都不可以指定了
#include<iostream> using namespace std; void Func(int a = 10, int b = 20, int c = 30) { cout << a << ' '; cout << b << ' '; cout << c << ' '; cout << endl; } int main() { Func(); Func(1); Func(1, 2); Func(1, 2, 3); return 0; }
比如说像下面这种是错误的