主要内容:
1.命名空间
2.c++的输入与输出
3.缺省参数
4.函数重载
5.引用
6.内联函数
7.auto关键字
8.基于范围的for循环
9.空指针–nullptr


1.命名空间

1.1为什么要有命名空间呢?:1.全局作用域里面的变量,类(后面会提到的)和函数的名称可能会与 关键字 冲突  2.在进行一个大工程的时候,通常是几个程序原分开写一部分功能,然而当几个文件合并的时候,那些定义在全局域里面的变量,类,核函数名称可能会相互冲突,所以就引出了我们的命名空间

1.2命名空间的作用: 命名空间就是对标识符的名称进行本地化,以避免命名冲突或者名字污染。

1.3命名空间的定义: 命名空间需要一个关键词namespace(顾名思义,命名空间),然后接上你给这个命名空间取的名字,最后再加上{}即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define _CRT_SECURE_NO_WARNINGS 1
namespace rxj//命名空间内部可以定义函数/变量/类型
{
int rand = 10;
namespace rxj2//命名空间可以嵌套
{
int x = 3;
}
}
int main()
{

return 0;
}

注意:1.命名空间要定义在全局域中   2.命名空间可以进行嵌套   3.命名空间同一个工程中如果遇到相同名称的命名空间, 编译器 会把他们合到一起

补充 : 预编译阶段,#include 会将头文件的内容直接替换到当前文件中(类似 “文本拷贝”),这是预处理的基本行为,C++ 标准库的标识符(如 coutcinvector 等)通常定义在 std 命名空间中!!!!!所以我们在包含了有文件以后如果要是用哪个变量,还需要使用作用域限定符

1.4命名空间的使用的三种方式:
1.命名空间的名称+::(作用域限定符)+要 使用 的变量的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
namespace rxj//命名空间内部可以定义函数/变量/类型
{
char name[20] = "rxj is your friend";
int age = 19;
int height = 172;
}
//using std::cout;
//using std::endl;
int main()
{
std::cout << rxj::name << std::endl;//在这里我们使用的是命名空间名称+作用域限定符+变量名称
return 0;
}

2.可以在全局中使用using将命名空间中的某个成员单独进行引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
namespace rxj//命名空间内部可以定义函数/变量/类型
{
char name[20] = "rxj is your friend";
int age = 19;
int height = 172;
}
using std::cout;////在这里我们使用的是using+命名空间名称+作用域限定符+变量名称
using std::endl;
int main()
{
cout << rxj::name << endl;
return 0;
}

3.可以直接展开某个命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
namespace rxj//命名空间内部可以定义函数/变量/类型
{
char name[20] = "rxj is your friend";
int age = 19;
int height = 172;
}
using namespace rxj;
using namespace std;
int main()
{
cout << rxj::name << endl;
return 0;
}

补充:

1.编译器搜索变量的顺序:局部域->全局域

2.我们一般不会把命名空间域给展开,因为命名空间本来就是帮助我们在全局中定义一些名字冲突的变量,类型或者函数,所以我们一般都指定访问


2.c++的输入&输出

说明:1.C++的输入输出更方便,可以自动识别变量的类型

           2.cout是流输出运算符,cin是流输入运算符,他们包含在这个头文件中,

在使用时我们通常会用到<<流插入运算符,>>流提取运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
namespace rxj//命名空间内部可以定义函数/变量/类型
{
char name[20] = "rxj is your friend";
int age = 19;
int height = 172;
}
namespace kym
{
char name[20];
int age;
int height;
}
using std::cin;
using std::cout;
using std::endl;
int main()
{
cin >> kym::name;//在输入字符串的时候不能有空格,否则就自动切换到了下一行
cin >> kym::age;
cin >> kym::height;
cout << kym::name << " " << kym::height << " " << kym::age << endl;
return 0;
}

3.缺省参数

缺省函数的概念: 给函数的形参指定一个确定的值,在调用该函数的时候,如果没有指定实参,那么就使用形参。

注意:1.缺省参数的值只能在函数声明的时候给 ,原因:在 预处理 阶段编译器会把头文件展开,展开以后各自的.c++文件会单独进行编译,并不会合到一起,如果你将缺省参数给了定义,那么在编译阶段会出错,因为在语法上面就不对了,你明明定义这个函数有两个参数,你却只传了一个参数

           2.半缺省参数必须要从右往左依次给出,因为传参的时候默认传给的是没有定义的参数。

           3.缺省值必须是常量或者全局变量


4.函数重载

函数重载的概念:c++允许在同一个作用域中声明几个同名函数,这些函数的形参列表不同(参数个数,类型,类型顺序)

注意:和返回值没有关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void fun(int a, char* b)
{
std::cout << "void fun1(int a, char b)" << std::endl;
}

void fun(char* a, int b)
{
std::cout << "void fun1(int a, char b)" << std::endl;
}

int main()
{
char a[20] = "namespace";
int b = 10;
fun(a, b);//void fun1(int a, char b)
fun(b, a);//void fun2(char a, int b)
return 0;
}

5.引用

引用就是给变量取一个别名,你对引用进行操作的时候,其实就是对变量进行了操作

注意事项:

1.在定义引用的时候就必须初始化

2.一个变量可以有多个引用

3.引用一旦引用一个实体,就不能再引用其他实体

4.在引用过程中,权限只能平移或者缩小,不能放大

5.引用的类型必须要和实体的类型保持相同

一些重要的事例:

eg1:引用的权限不能放大

1
2
3
4
5
6
int main()
{
const int a = 10;
int& b = a;
return 0;
}

注:a这个变量的值不能更改,如果按照上面的方式去引用a明显破坏了语法规则,所以编译器是不能通过的

eg2:引用权限不能放大的变式(类型转换)

1
2
3
4
5
6
7
8
int main()
{
double a = 10.1;
int b = a;//√
const int& c = a;//√
int& d = a;×,因为中间生成的临时变量具有常性,所以要用const修饰一下
return 0;
}

因为在类型转换的时候,会生成一个中间的临时变量,临时变量具有常性,可以认为是一个常量

引用的使用场景:

1.做参数:就相当于在c语言里面的指针,看下里面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Swap1(int* a, int* b)
{
int c = *a;
*a = *b;
*b = c;
}
void Swap2(int& a, int& b)
{
int c = a;
a = b;
b = c;
}
using namespace std;
int main()
{
int a = 10, b = 20;
Swap1(&a, &b);
cout << "Swap1 " << a << " " << b << endl;
Swap2(a, b);
cout << "Swap2 " << a << " " << b << endl;
return 0;
}

在传参数的时候可以不用取地址了,更加方便一点

2.做返回值:在学习引用返回之前我们先了解一下值返回,值返回的本质其实是拷贝,也就是先生成一个临时变量,然后再返回回去,然而我们的引用返回就有点像是指针返回了那个地址处的值,这样做可以提升效率,但同时伴随着危险,也就是非法访问。【所以引用返回有几个使用条件:该变量不能是临时变量,也就是再出了作用域之后,该空间不会被操作系统收回。(符合这种条件的有静态变量,也有用malloc开辟的空间)】

引用和指针的不同点:

1、引用在概念上是定义了一个变量的别名,而指针式存储一个变量的地址

2、引用在定义的时候必须初始化

3、引用在初始化时引用过一个实体以后,就不能再引用其他的实体,而指针可以在任何时候指向任何同一类型的实体

4、没有NULL引用!!!(也就是说必须要初始化成一个不为NULL的实体)但有NULL指针

5、在sizeof中的含义不同,引用在sizeof中指的是引用对象类型的大小,然而指针则是地址空间所占字节个数的大小

6、引用自加表示实体+1,然而指针自加表示指针向后偏移一个类型的大小!!!!!

7、在引用过程中,权限可以平移或者缩小,但是不能够放大!!!!!!!!

8、有多级指针但是没有多级引用

9、引用比指针相对来说更安全

10、访问实体的方式不同,指针需要解引用(显式),然而引用是编译器自己处理的


6.内联函数

概念:以inline修饰的函数叫内联函数,c++编译器会在调用内联函数的地方展开,这样就没有 函数调用 建立栈帧的开销(所以是用范围就是短小函数的频繁调用)

内联函数其实就是一种空间换时间的做法,底层原理是在编译阶段经函数调用替换成函数体,但是可能会使目标文件变大。而且inline只是一种请求没如果函数的行数过多,比那一期可能不会采用内联函数

注意:内联函数的生命和定义不要分开


7. auto 关键字

auto的概念:auto是一个类型指示符,在编译阶段编译器会自动推导auto修饰的变量的类型。

值得注意的是:在使用auto定义一个变量的时候,必须要进行初始化,因为在编译阶段编译器就是要根据这个表达式来推断auto的实际类型。所以auto并不是一种类型的声明,而是一种类型声明时的占位符,在编译阶段编译器会把auto替换成变量实际的类型。

auto的使用细则:

1.auto声明指针类型时,auto*和auto没有任何区别,但是auto声明引用类型时必须要加上一个&

2.在同一行使用auto定义多个变量时,那些变量的类型必须相同,否则编译器会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

1
2
3
4
5
6
>using namespace std;
int main()
{
auto c = 10, d = 20;
auto e = 10, f = 1.1;
}

auto不能推导的场景 :

1、auto不能作为函数的参数

因为之前讲过编译器是根据表达式来推断变量类型的

2、auto不能不能用来声明数组

比如:auto b[] = {4,5,6}是不行的


8.基于范围的for循环

如果要访问一个有范围的集合,可以用for循环,for循环后的括号内部由:隔开,第一部分是迭代的变量,第二部分是迭代的范围

注意:for循环的使用条件:迭代的范围必须是确定的

1
2
3
4
5
6
7
8
9
10
11
12
void rxj(int a[])
{
for (auto e : a)//这个是错误的,因为a的范围不确定
{
printf("%d ", e);
}
}
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
rxj(a);
}

9.空指针–nullptr

在c++中,NULL被定义为字面常量0,或者定义成无类型指针常来那个,但是编译器在默认情况下会将其定义成0,会产生一些麻烦,所以我们就引出了空指针nullptr