注意:代码环境为 VS2022

# C++ 介绍

C++(C Plus Plus),是由微软 (Microsoft) 公司基于 C 语言扩充编写的一门语言。

C++ 是一门面向对象的语言

其中部分语法与 C 语言相似,可以参考 C 语言笔记 (有些部分我直接从 C 语言笔记里复制过来了,我会将其标出)。

# Hello World

1
2
3
4
5
6
#include<iostream>
using namespace std;
int main(){
cout << "Hello World!" << endl;
return 0;
}

请自行安装 VS2022 (或者其他 ide),可以参考 C 语言笔记的内容。

输入上面的代码,实现第一个 C++ 程序

# C++ 语法

# 注释

1
2
3
4
//单行注释
/*
多行注释
*/

# 位 (bit) 与字节 (byte)

在日常使用计算机的时候,我们通常会看到一些数据单位,如 kb,MB,GB 等

其中 kb 指的是 kilo byte (千字节),mb 指的是 mega byte (兆字节)。诸如此类的还有 TB,PB,EB 等。

计算机是电子产品,对其来说,只存在开路 (0) 与闭路 (1)

我们将一个最基础的单位 (0 或 1) 称为位 (bit),将 8 个位合在一起称为一个字节 (byte)。

当我需要将整数 8 存入计算机时,8 会被转换为二进制数 1000 被存入到内存中。

这在 C 语言中,通常会占用 4 个字节,也就是 32 位。

# 数据类型

# 基本数据类型:

基本数据类型包括:

数值类型:

  • 整型
  • 浮点型

字符类型 (char)

其中整型包括:短整型 (short)、整型 (int)、长整型 (long)

其中浮点型包括:单精度型 (float)、双精度型 (double)

整型,即为整数。 浮点型,即为小数。

短整型、整型与长整型的差距在于其存储时,占用的字节数

类型 字节数 存储数值的范围
short 2 -2^15, 2^15-1
int 4 -2^31, 2^31-1
long 8 -2^63, 2^63-1

这是大致的内存占用情况,需要注意的是,这些数据类型在不同的系统上,会占用不同的字节数,而非固定。

通过类型占用的字节数,可以计算出其存储数值范围。

例如:int 是 4 个字节,也就是 32 位,因为其存储的是二进制数,所以理论上范围应该是 [-2^32,2^32 - 1] 但第一位要用于存储其符号,也就是数值是正还是负,所以占用了一个位,则其范围变为 [-2^31,2^31 - 1],正数范围需要减一是为了存储 0

单精度浮点型与双精度浮点型的区别:

类型 字节 存储数值的范围
float 4 -3.4*10^38, 3.4*10^38
double 8 -1.7*10^308, 1.7*10^308

其中单精度浮点数可以存储到小数点后 6 位数字,而双精度浮点数可以存储到小数点后 15 位数字。

打印时,默认保留 6 位小数

# 布尔类型

需要注意,C 语言并没有布尔类型,但是程序中经常会用到布尔类型,因此我在这里单独拿出来使用

在 C89 标准时,C 语言没有布尔类型,在后来的 C99 标准时,才引入了布尔类型。


布尔类型是只有两种值的数据类型,包含真 (True) 与假 (False)

在 C 语言中,通常使用 0 作为布尔类型的 False,非 0 作为布尔类型的 True

# 构造类型

构造类型包括:

  • 数组 (array)
  • 结构体 (struct)
  • 共用体 (union)
  • 枚举类型 (enum)

这四种类型会在后面讲到。

# 指针类型

指针类型通常占用 4 个字节,存储十六进制数,用于保存地址值,具体会在指针的部分讲到。

# 空类型

型如其名,空 (void)

# 变量

这块儿从 C 语言笔记那里复制过来的所以用的输出方式可能是 printf 而不是 cout

# 声明变量与赋值:

创建一个新的源文件,并将先前的第一个程序全部注释掉:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main(){
int var1;//声明一个整型变量
var1 = 0;//给var1赋值
float var2 = 0.123f;//声明并初始化一个单精度浮点型变量
double var3 = 0.123456;//声明并初始化一个双精度浮点型变量
char var4 = 'c';//声明并初始化一个字符型变量
printf("%d,%f,%lf,%c", var1, var2, var3, var4);//使用占位符%d,%f,%lf与%c输出相应变量。
return 0;
}

声明变量,即创建一个变量,然后使用 = 为其赋值。

注意:赋值行为是将等号右边的数值分配给等号左边的变量,不能写反!

第一次给变量赋值的行为,被叫做初始化,声明与初始化可以写在同一行,如 var2 与 var3

注意 2:请不要在未初始化的情况下调用变量,会导致程序错误!

IDE 会默认浮点数为双精度浮点数,在浮点数后面添加 f (或 F),标志其为单精度浮点数 (也可以不加)。

打印时,使用占位符进行占位,占位符与后面的变量需要一一对应。其中 % d 为整型的占位符,% f 为单精度浮点型的占位符,% lf 为双精度浮点型的占位符。

# 变量名:

变量命名时需要遵循一定的规则:

  • 变量名只能包含字母、数字、下划线和 $
  • 变量名只能以字母、下划线或者 $ 开头
  • 变量名不能使用关键字
  • 变量名严格区分大小写
1
2
3
4
5
6
7
8
9
//例如
int 123abc;//这是一个不符合语法的变量声明,会导致报错!
int int;//这是一个非法声明!
int 变量;//非法声明!

int var;//合法声明
int _var;//合法声明
int $var;//合法声明
int VAR;//合法声明,但需要注意,VAR不等于var

关键字:C 语言使用到的单词,例如:int,float,void 等,在起变量名或者函数名时需要避开。

关键字并不需要记忆,在 VS2022 中,当使用到关键字时,会被特殊的颜色标出。


除了必要的语法外,我们在日常编程中也有一些默认规则。

  • 变量名要做到见名知意
  • 变量名遵循驼峰法,或者下划线法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//例如
int var;//var是variable(变量)的缩写
int count;//count(计数),通常用于计数
int sum;//sum(和),通常用于求和

//上面的变量名,看到名字便知道其作用。
//需要使用两个及以上的单词去描述变量时,通常使用驼峰法或下划线法命名
//驼峰法即:单词的首字母大写,如:
int studentId;//学号
char studentAddress;//学生家庭地址

//下划线法即:使用下划线分割单词,如:
int student_name;//学生姓名
int student_class;//学生班级

驼峰法与下划线法的选择看个人喜好。

# 运算符

需要注意,运算符区分优先级,大致为:

数值运算符 > 比较运算符 > 逻辑运算符 (不绝对)

其中逻辑运算符中!> && > ||

具体优先级可以自行搜索

# 数值运算符

C 语言中提供一些数值运算的符号,如下:

符号 作用 使用方法
= 赋值运算符,将等号右侧的值赋给等号左边的变量 var = 10
+ 加运算符 var = 1 + 2
- 减运算符 var = 2 - 1
* 乘运算符 var = 5 * 10
/ 除运算符 var = 10 / 5
% 求余数运算符 var = 11 % 5 (var 的值为 1)
+= 可以将右侧式子理解为 var = var + 10 var += 10
-= 可以将右侧式子理解为 var = var - 10 var -= 10
*= 可以将右侧式子理解为 var = var * 10 var *= 10
/= 可以将右侧式子理解为 var = var / 10 var /= 10
%= 可以将右侧式子理解为 var = var % 10 var %= 10
++ 自增运算,相当于 var = var + 1 var++(或者 ++var)
自减运算,相当于 var = var - 1 var–(或者–var)

注意:var++ 与 ++var 使用方式并不相同,var-- 与 --var 同样

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<stdio.h>
int main(){
int var = 1;//声明并初始化整型变量var

var += 10;//加运算示例
printf("%d\n", var);

var -= 10;//减运算示例
printf("%d\n", var);

var *= 10;//乘运算示例
printf("%d\n", var);

var /= 10;//除运算示例
printf("%d\n", var);

var = 11;//求余运算示例
var %= 10;
printf("%d\n", var);

printf("%d\n", var++);//后++示例
printf("%d\n", ++var);//前++示例
return 0;
}

运行上述代码,可以发现打印结果为:

1
2
3
4
5
6
7
11	//加法示例打印,var为1,var+=10即为var=var+10,所以结果为11
1 //减法示例打印,var此时为11,var-=10即为var=var-10,所以结果为1
10 //乘法示例打印,var此时为1,var*=10即为var=var*10,所以结果为10
1 //除法示例打印,var此时为10,var/=10即为var=var/10,所以结果为1
1 //求余示例打印,计算前给var赋值为11,所以var此时为11,var%=10即为var=var%10,所以结果为1
1 //后++示例
3 //前++示例

需要注意的是后 ++ 与前 ++ 的区分:

使用后 ++ 时,是首先使用变量 var,再进行自增,所以当 var=1 时,使用 var++ 进行打印,首先使用 var 打印出 1,然后对其进行自增,则 var=2.

使用前 ++ 时,是首先自增,再使用变量 var,所以当 var=2 时,使用 ++var 进行打印,首先自增,使 var 变为 3,然后对 var 进行打印,打印出数字 3.

# 比较运算符

C 语言中提供一些比较运算的符号,如下:

符号 作用 使用方法
< 判断左值是否小于右值,返回布尔类型 number1 < number2
> 判断左值是否大于右值,返回布尔类型 number1 > number2
<= 判断左值是否小于或等于右值,返回布尔类型 number1 <= number2
>= 判断左值是否大于或等于右值,返回布尔类型 number1 >= number2
== 判断左值是否等于右值,返回布尔类型 number1 == number2
!= 判断左值是否不等于右值,返回布尔类型 number1 != number2

注意:C 语言自带的数据类型中,并没有真正的布尔类型,只是使用零和非零模拟出的布尔类型

# 逻辑运算符

C 语言中提供一些逻辑运算的符号,如下:

符号 作用 使用
&& 逻辑与,判断左值与右值是否都为真,返回布尔类型 a && b
|| 逻辑或,判断左值或右值是否为真,返回布尔类型 a || b
! 逻辑非,取其相反值,返回布尔类型 (若 a 为 True,则!a 为 False) !a

逻辑与,逻辑或,逻辑非的运算结果如下:

&& 运算 b = True b = False
a = True True False
a = False False False

逻辑与:全真才真,一假皆假。

|| 运算 b = True b = False
a = True True True
a = False True False

逻辑或:一真即真,全假才假。

短路现象:

对于 a&&b,当 a 为假的时候,我们便可以立即断定 a&&b 就是假的,那么程序将不会再判断 b,这种现象被称为短路现象。

同样的,对于 a||b,当 a 为真,则 a||b 就一定是真的,程序也不会再判断 b。

# 位运算符

C 语言中提供一些位运算的符号,如下:

符号 作用 使用
& 对左值与右值进行与运算 a & b
| 对左值与右值进行或运算 a | b
^ 对左值与右值进行位运算 a ^ b
<< 对左值进行左移运算,左移位数取决于右值 1 << 5
>> 对左值进行右移运算,右移位数取决于右值 32 >> 3

使用如下代码进行测试:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main() {
int a = 1, b = 5;
printf("%d\n", a & b);
printf("%d\n", a | b);
printf("%d\n", a ^ b);
printf("%d\n", 1 << 5);
printf("%d\n", 32 >> 3);
return 0;c
}

会发现,最终输出的结果分别是 1,5,4,32 和 4 接下来我会对位运算做详细解释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//因为会有对齐操作,为了避免转换为网页时缩进错序,所以使用代码块进行解释
//a的值是1,其对应的二进制数可以写为001,b的值是5,其对应的二进制数可以写为101
//当a和b进行与运算时,我们首先将其对齐,如下
//001
//101
//然后逐个按位比较,如果都是1,那么结果取1,否则取0,于是得到结果
//001
//转化为十进制数后,值是1,所以第一个printf的输出值为1

//而或运算则是:如果都是0,那么结果取0,否则取1。
//001
//101
//101
//按照或运算的规则,可以得到最后的结果为101,转化为十进制则是5,所以第二个printf的输出值为5

//异或运算则是:如果相同则取0,不同则取1
//001
//101
//100
//按照异或运算的规则,可以得到最后的结果为100,转化为十进制则是4,所以第三个printf的输出值为4

//左移运算:将二进制数左移x位,移动后在后面补0
//譬如对于1,其二进制为1,将其左移5位,然后在后面补0,那么结果就是10 0000,转化为十进制数则是32

//右移运算:将二进制数右移x位,移出部分删去
//譬如对于32,其二进制为10 0000,将其右移3位,那么就变为100,转化为十进制则是4

# 数据类型转换

将低精度值赋值给高精度值时,数据会自动转换类型,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
int main(){
char c = 'a';//声明并初始化字符型变量c
int number1 = 10;//声明并初始化整型变量number1
float number2 = 10.10;//声明并初始化单精度浮点型变量number2
double number3 = 100.100;//声明并初始化双精度浮点型变量number3

//当把高精度的数值,赋给低精度的变量时,会导致警告,例如:
number1 = number2;
//因为整型并不存在小数部分,所以将浮点型赋值给整型时,会导致小数部分的数据丢失,因此会被警告
//将高精度数值赋值给低精度变量时,可以强制转换其数据类型,例如:
number1 = (int)number2;//在前面使用(数据类型),来强制转换变量的数据类型

//而当低精度的数值,赋给高精度的变量时,则会自动转换其数据类型,例如:
number2 = number1;//并不需要强制转换

//基本数据类型中,可以自动转换的数据类型级别,大致如下:
//double > float > unsigned long > long > unsigned int > int > short
number3 = number2 = number1 = c;
printf("%lf", number3);//打印结果应为97.000000

return 0;
}

需要注意的是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
当浮点型被转换为整型时,并不会采取四舍五入的方式,而是截断。

即var = 1.1得到的结果为var = 1

var = 1.6得到的结果也为var = 1
*/
#include<stdio.h>
int main(){
int var1 = 1.1;
int var2 = 1.6;
float var3 = 3 / 5;

printf("%d,%d,%f", var1, var2, var3);
return 0;
}

得到结果为 1,1,0.000000

其中 var1 与 var2 已经解释过了,而其中为 float 类型的 var3 结果却是 0.000000

这是因为参与计算的数值均为整型,即 3 / 5,运算出来的返回结果也只能是整型,得到的结果是被截断过后的整型 0,最后打印出的结果是 0.000000

若要返回结果为浮点型,则需要使用浮点型数值参与运算,比如改为

1
float var3 = 3.0 / 5;

# 分支语句语句

# if、else if、else 语句

在生活中,我们经常会遇到分支情况,例如:

如果沙县小吃比大盘鸡更便宜,我今天就去吃沙县小吃,否则的话就去吃大盘鸡。

我们注意到,在这句话里出现了两条分支:

  1. 去吃沙县小吃
  2. 去吃大盘鸡

而我们根据:哪个更便宜?这个条件对我们要做的选择进行判断。

在编程中,我们也可以实现类似的分支情况,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;
int main() {
int sha_xian = 20, da_pan_ji = 58;
if (sha_xian < da_pan_ji) {
cout << "今晚吃沙县" << endl;
}
else {
cout << "今晚吃大盘鸡" << endl;
}
return 0;
}

这部分代码,相信许多人只是读一遍,便能够理解,接下来我要详细介绍 if 语句的细节,首先是语法:

1
if (布尔类型) {需要执行的代码块}

这是一个 if 语句的语法,如果 if 后的代码块仅仅只有 1 句 (1 个分号为 1 句),那么可以不写大括号,如下:

1
2
if (布尔类型)
cout << "只有一句代码时,可以不写大括号" << endl;

当括号内的布尔类型为真时,执行这条 if 语句,为假时,则不会执行。

可以使用上面提到的比较运算符来获得布尔类型,如上面的沙县、大盘鸡比较代码。

需要注意的是,if 语句可以单独使用,而 else 语句不可以,else 语句必须要有对应的 if 语句。

1
2
3
4
5
6
//这样写是正确的
if (布尔类型){需要执行的代码块}
else {需要执行的代码块}
//---------------------------------
//这样写是错误的
else {代码块}

当 if 内的布尔类型为假时,程序便会不执行 if 语句,直接进入 else 语句

如果 else 后面的语句仅仅只有 1 句,也可以不写大括号

else 语句的匹配规则遵循就近原则:

1
2
3
if (布尔类型) {代码块}//这个if语句没有else
if (布尔类型) {代码块}//下面的else语句会匹配离它最近的这个if语句
else {代码块}

回到上面的沙县与大盘鸡案例,我们可以想到,其价格的比较结果并非一定只有两种,还会有相同的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
int main() {
int sha_xian = 20, da_pan_ji = 58;
if (sha_xian < da_pan_ji) {
cout << "今晚吃沙县" << endl;
}
else if (sha_xian == da_pan_ji) {
cout << "吃哪个都可以" << endl;
}
else {
cout << "今晚吃大盘鸡" << endl;
}
return 0;
}

可以在 if 的下面添加 else if 语句来判断这种情况,else if 语句的作用类似于 if 语句,但是不能单独使用

else if 可以存在不止一句

同样的,当 else if 语句后面只有一条语句时,可以不写大括号。

# switch 语句

在生活中,除了如同 if、else 这种分支,还存在多分支的情况,例如:

  • 如果今天是周一,我就去学 C 语言
  • 如果今天是周二,我就去学 C++
  • 如果今天是周三,我就去学 Java
  • 如果今天是周四,我就去学 Python
  • 如果今天是周五,我就去学 C#
  • 如果今天是周六,我就出去玩
  • 如果今天是周日,我就睡懒觉

当然,这种多分支的情况可以使用多个 else if 去实现,但是当分支过多,if 语句的效率通常不如 switch 语句。

首先介绍一下 switch 语句的语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
switch(变量){
case1:
执行代码块;
break;
case2:
执行代码块;
break;
case3:
执行代码块;
break;
default:
执行代码块;
}

switch 后面的括号中的变量可以是整型或者字符型。

当变量的值等于值 1 时,便会执行 case 值 1: 后面的代码块。

需要注意的时,case 语句后面的是冒号而不是分号。

当 case 语句后的代码块被执行完后,如果有 break 语句,则会终止 switch,如果没有 break 语句,则会继续向下执行。

例如:

1
2
3
4
5
6
7
8
switch(key){
case 1:
printf("1");
case 2:
printf("2");
case 3:
printf("3");
}

对于上面的这个 switch 语句来说,如果 key 的值为 1,则最后的打印结果会是 123

如果 key 的值为 2,则最后的打印结果会是 23,这种现象被称为穿透效果

default 语句并非必要,可以不写

default 语句类似于 if 语句中的 else,当所有情况都不被匹配到时,会被执行。

如果 default 上面的语句并没有写 break;那么 default 也会被穿透。


接着我们回到上面的问题,如果使用 switch 语句实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<iostream>
using namespace std;
int main() {
int date = 1;
switch (date) {
case 1:
printf("今天是周一,我去学C语言");
break;
case 2:
printf("今天是周二,我去学C++");
break;
case 3:
printf("今天是周三,我去学Java");
break;
case 4:
printf("今天是周四,我去学Python");
break;
case 5:
printf("今天是周五,我去学C#");
break;
case 6:
printf("今天是周六,我出去玩");
break;
case 7:
printf("今天是周日,我睡懒觉");
break;
default:
printf("今天周几都不是,什么都不干");
}
return 0;
}

我们通过另一个案例来感受一下穿透效果:

小明的爸爸许诺给小明:

  • 如果小明的期末成绩高于 60 分,便给他 100 元钱
  • 如果高于 80 分,便给他买手机、和给他 100 元钱
  • 如果高于 100 分,便给他买电脑、手机,而且给他 100 元钱

使用 switch 实现的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
int main() {
int score = 100;
switch (score / 10) {
case 10:
printf("给小明买电脑\n");
case 9:
case 8:
printf("给小明买手机\n");
case 7:
case 6:
printf("给小明100元钱\n");
}
return 0;
}

通过这个代码,我们很轻松的实现了小明爸爸的许诺这个案例。

# 指针 (从 C 中复制)

# 初识

指针是 C 语言中一个十分重要的概念。

指针的长度为 4 个字节,内部存储的是一个十六进制数。

在数据类型的后面添加一个星号,以创建对应的类型指针,如下:

1
2
int * int_pointer;//声明一个整型指针
float * float_pointer;//声明一个浮点型指针

指针存储的十六进制数,是内存中对应的位置,也被称为地址

我们在 scanf 中见到过 & 符号,这个符号用于取出一个变量的地址,如下:

1
2
int number = 0;//声明并初始化一个整型
int * int_pointer = &number;//声明并初始化一个整型指针

地址是一个十分形象的名字,正如我们每个人都有自己的家庭地址,地址记录了一个变量在内存中存储的位置。

指针的初始化通常使用 NULL 来进行:

1
int * int_pointer = NULL;//声明并初始化一个空指针

当你创建了一个指针,但暂时不知道需要让它指向谁时,可以赋给其 NULL (空指针) 来避免错误调用未初始化的指针。

可以通过解引用符号 (星号),来调用一个指针所指向的地址中保存的值,如下:

1
2
3
4
5
int number = 0;//声明并初始化一个整型
int * int_pointer = &number;//声明并初始化一个整型指针
printf("%d\n", *int_pointer);//调用指针指向地址中保存的值
//也可以打印指针本身
printf("%p\n", int_pointer);//打印的结果是一个十六进制数

指针也可以做基本的运算,例如使用指针做自增运算:

1
2
3
int number = 0;//声明并初始化一个整型
int * int_pointer = &number;//声明并初始化一个整型指针
int_pointer++;//指针自增

指针自增时,会根据其类型增加相应的字节数,比如整型指针,实际是自增了一个 int (4 个字节) 的大小。

这种使用方式通常配合内存管理 (malloc、free) 或者数组使用,在后面会详细介绍

注意!这里仅做一个示范,实际上这样使用是错误的,会导致指针指向未知的内存空间。

# 值传递与指针传递

根据已经学习过的知识,我们可以简单的写出一个交换 a,b 变量值的程序:

1
2
3
4
5
6
7
8
9
#include<stdio.h>
int main() {
int a = 10, b = 20;
int temp = a;//使用中间变量temp保存a的值
a = b;//将b的值赋给变量a
b = temp;//将保存的值赋给b,完成交换
printf("%d,%d", a, b);//打印检测
return 0;
}

然后将其封装成一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
void Swap(int x, int y);
int main() {
int a = 10, b = 20;
Swap(a, b);
printf("%d,%d", a, b);//打印检测
return 0;
}
void Swap(int x, int y) {
int temp = x;
x = y;
y = temp;
}

通过打印,我们可以发现,a 与 b 的值并未实现交换。

这与值传递、地址传递有关。

对于一个函数的形参 (形式参数,即上面函数中的 x 和 y),在调用函数时,会自动生成新的变量 x, y 然后把 a 和 b 的值赋给对应的形参。

这样我们就不难理解为什么 Swap 函数并未改变 a 与 b 的值,因为函数从始至终都未与变量 a、b 打过交道

那么如果我想要使用函数修改一个变量的值,该怎么做?那就是传递一个变量的地址,修改成如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
void Swap(int* x, int* y);//需要的参数修改为指针
int main() {
int a = 10, b = 20;
Swap(&a, &b);//使用&符号,取出a与b的地址,传递过去
printf("%d,%d", a, b);//打印检测
return 0;
}
void Swap(int* x, int* y) {
int temp = *x;//使用*来调用指针指向的地址中保存的值
*x = *y;
*y = temp;
}

因为每个变量对应的地址是唯一的,所以使用指针通过地址对值进行修改,就一定可以修改到目标变量。

另外,指针传递也可以减少值的复制,这一应用会在数组与结构体中体现。

# 函数指针与回调函数

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

函数指针可以像一般函数一样,用于调用函数、传递参数。

函数指针变量的声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
函数返回值类型 (*函数指针名)(参数类型列表) = 函数名;
#include<stdio.h>
int max(int a, int b){//声明并实现一个函数,该函数的作用:在a与b中取出更大值并返回
if (a > b)
return a;
return b;
}
int main(){
int x = 10, y = 20;
int (*pointer_max)(int, int) = max;//使用示例
printf("max is %d", pointer_max(x, y));//使用函数指针调用函数
return 0;
}

回调函数:当函数所需的参数列表中,包含函数指针时,该函数被称为回调函数。

即在函数中,通过函数指针调用另一个函数,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
void print_Function() {
cout << "printFunction被调用\n" << endl;
}
void callback_Function(int times, void (*p_f_parameter)(void)) {//回调函数
for (int i = 0; i < times; i++)//使用for循环,调用times次函数指针所指向的函数
p_f_parameter();
}
int main() {
callbackFunction(10, print_Function);//这里仅写函数名,不能加括号,加上括号相当于调用函数,给了一个空值(函数返回为空)
return 0;
}

其中 callback_Function 为回调函数。

# 多级指针

正如每个变量都有自己对应的地址,指针变量作为一个存储指针的变量,也有自己的地址。

我们可以使用一个二级指针来保存指针变量对应的地址,如下:

1
2
3
int number = 1;//声明并初始化一个整型变量
int * int_pointer = &number;//声明并初始化一个指针变量
int ** level_2_pointer = &int_pointer;//声明并初始化一个二级指针变量

根据星号的数量可以判断一个指针的级别,以此类推,还有三级、四级、多级指针。

# 指针与常量

const 关键字与指针在一起使用时,有多种使用方法:

1
2
3
4
const int * p;//常量指针
int const * p;//常量指针

int *const p;//指针常量

前两者的效果是相同的,常量指针正如同它的名字,这是指向常量的一个指针

对于常量指针来说,是不可以通过解引用符 (星号) 去改变其地址中保存的值的,因为地址中保存的值是一个常量,常量是不可修改的

1
2
3
4
5
const int *p;//声明常量指针p
int a = 4;//声明并初始化变量a
p = &a;//将变量a的地址赋值给p
*p = 5;//错误,不能通过指针p来改变值
//但是这里可以通过修改a来修改值,因为a是变量

对于常量指针来说,指向的地址中存储的值不可修改,但指向的地址是可以修改的,如下:

1
2
int b = 5;//再声明并初始化一个变量b
p = &b;//将b的地址复制给p

而指针常量,指的是指针本身是一个常量,即指针指向的地址不可改变,但指向地址中存储的值可以改变,例如:

1
2
3
4
int a = 4, b = 5;
int *const p = &a;//和使用const定义常量一样,声明的同时就需要初始化
*p = 5;//这是被允许的
p = &b;//错误,不能修改一个常量

除此之外,还有指向常量的指针常量,即为以上二者的结合

1
const int *const p;//它存储的地址不允许被改变,地址中保存的值也不允许被改变

const 修饰词可以在函数之间传递地址时,锁定数据,防止误操作

# 结构体 (从 C 中复制)

# 初识

C 中的类取代了结构体的作用,因此结构体在 C 中相对于 C 作用小很多。

定义结构体的语法:

1
2
3
4
5
6
7
struct struct_tag {//结构体标签
member_element;//结构体成员元素
member_element;
member_element;
...
}variable_struct;//结构变量
//结构变量不能与结构体标签同名

一般情况下,结构体标签、成员元素、结构变量,这 3 部分至少要出现两个。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//形式1
#include<stdio.h>
struct {
int a;
char b;
float c;
}my_struct;
//使用这种方法,直接定义了一个结构变量my_struct,但是不再能创建第二个结构变量
//可以直接调用
int main() {
my_struct.a = 0;//通过点(.)调用结构体的成员元素
my_struct.b = '\0';
my_struct.c = 0.0;
return 0;
}

或者这样定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//形式2
#include<stdio.h>
struct struct_tag {
int a;
char b;
float c;
};
//使用这种方法,定义了一个struct_tag的结构,需要声明变量再使用,如下:
int main() {
struct struct_tag my_struct;//声明一个结构变量
my_struct.a = 0;//通过点(.)调用结构体的成员元素
my_struct.b = '\0';
my_struct.c = 0.0;
return 0;
}

另外结构体经常会配合 typedef 使用,使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//配合typedef使用:
#include<stdio.h>
typedef struct {
int a;
char b;
float c;
}struct_name;
//使用这种方式,定义了一个叫做struct_name的新变量类型,可以把它当成int之类的变量类型使用
int main() {
struct_name my_struct;//声明一个结构变量
my_struct.a = 0;//通过点(.)调用结构体的成员元素
my_struct.b = '\0';
my_struct.c = 0.0;
return 0;
}

# typedef

使用 typedef 关键字可以自定义数据类型,如结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<string.h>
typedef struct {
int id;
char name[10];//长度为10的字符数组
}Student;

int main() {
Student student;//声明一个结构变量
student.id = 123;
strcpy(student.name, "张三");//使用string.h头文件下的strcpy函数操作字符数组
printf("学生:%s, 学号:%d", student.name, student.id);//打印测试
return 0;
}

也可以用于给原有数据类型起别名:

1
2
3
4
5
6
typedef int ElementType;//给int起别名为ElementType
int main(){
ElementType data = 123456;//等同于int data = 123456;
cout << data << endl;
return 0;
}

# 堆区开辟数据 (new 关键字)

使用 new 关键字开辟堆区数据,使用 delete 关键字进行释放

注意:开辟的堆区需要程序员手动释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
using namespace std;
int main(){
//使用: new type
int* example = new int(10);
cout << *example << endl;//打印10
delete example;//将example释放

int* example = new int[10];//创建一个长度为10的整型数组
for (int i = 0;i < 10;i++)//遍历数组赋值
example[i] = i;
for (int i = 0;i < 10;i++)//遍历数组打印
cout << example[i] << endl;
delete[] example;//释放数组
return 0;
}

# 引用 (起别名)

# 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//语法:
//数据类型 &别名 = 原名

#include<iostream>
using namespace std;

int main(){
int a = 10;
int &b = a;
//相当于新建一个指针,将a的地址赋给新指针
//注意:1.引用必须初始化 2.引用初始化后不可改变

int c = 20;

swap(a,c);//引用传递类似于地址传递,能修改原数据

cout << "a=" << a << endl;//a与c的值会交换
cout << "c=" << c << endl;

int &test01 = test();//返回number,其别名为test01

cout << "test01=" << test01 << endl;

test() = 1000;//因返回的是number,所以可以对number进行赋值

cout << "test01=" << test01 << endl;//对number赋值后,其引用test01的值也会改变

return 0;
}

//引用传递
void swap(int &a,int &b){//引用传递类似于地址传递,能修改原数据
int temp = a;
a = b;
b = temp;
}
//注意:不要返回局部变量,或者返回局部变量的引用

//函数可以作为左值
int& test(){
static int number = 10;
return number;
}

# 本质

1
2
3
4
5
6
7
8
9
10
11
12
//引用的本质就是指针常量
int a = 10;
int &ref = a;
//实际操作为 int* const ref = &a;
//指针常量的指向不可改变,所以引用不可更改
ref = 20;
//实际操作为 *ref = 20;

//对于函数
void func(int& ref){//实际操作为 int* const ref = &a;
ref = 100;
}

# 函数

函数基础参考 C 语言笔记

# 形参默认值与占位参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//C++函数的形参可以设定默认值,如
int example(int a,int b,int c = 20,int d = 30){//注意:有默认值的形参必须置后(写在最右边)
return a + b + c + d;
}//注意2:函数的声明与实现,仅能出现一次默认值设定

int main(){
cout << example(10,10) << endl;//打印40
return 0;
}

//函数占位参数
int func(int a,int){
//其中第二个int起到占位作用
//若有占位参数,则调用该函数时,需输入参数才可以调用。如func(10,10);
//占位参数可以有默认参数
}

# 函数重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//函数重载可以让函数名相同,提高复用性
//函数重载要求条件
//1.同一个作用域下
//2.函数名相同
//3.函数参数类型不同,或个数不同,或顺序不同
void func(){
cout << "func调用" << endl;
}
void func(int a){//参数个数不同
cout << "func(int a)调用" << endl;
}

void func(double a){//参数类型不同
cout << "func(double a)调用" << endl;
}

void func(int a,double b){
cout << "func(int a,double b)调用" << endl;
}

void func(double a,int b){//参数顺序不同
cout << "func(double a,int b)调用" << endl;
}

//注意:函数重载碰到默认参数
void func2(int a,int b = 10){
cout << "func2(int a,int b = 10)" << endl;
}
void func2(int a){
cout << "func2(int a)" << endl;
}
//对于以上写法,使用func2(10);调用函数时,会同时触发两种情况,导致错误

# 类与对象

C++ 面向对象的三大特性:封装、继承、多态

C++ 认为万事万物皆对象,对象上有其属性和行为

# 封装

封装的意义:

1. 将属性和行为作为一个整体,表现生活中的事物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//例(写一个圆类):
const double PI = 3.14;

class Circle{
//访问权限
public:
//属性 = 成员属性 = 成员变量
int c_r;//半径
//方法
double calculateZC(){
return 2 * PI * c_r;
}
}

int main(){
Circle c1;//创建对象(实例化)
c1.c_r = 10;//给圆类的属性赋值
cout << "圆的周长为:" << c1.calculateZC() << endl;//调用圆类的方法
}

//例2(学生类):
class student{
public://公共权限
string s_name;//姓名
int s_id;//学号
//方法
void showStudent(){//打印学生信息的方法
cout << "姓名:" << s_name << "学号:" << s_id << endl;
}
void setName(string name){//修改姓名的方法
s_name = name;
}
void setId(int id){//修改学号的方法
s_id = id;
}
}

2. 将属性和行为加以权限控制

1
2
3
4
//权限分为三种
//public 成员 类内可以访问,类外也可以访问
//protected 成员 类内可以访问,类外不可以访问。子类可以访问。
//private 成员 类内可以访问,类外不可以访问。子类不可以访问。

struct 与 class 的区别:struct 的默认权限为 public,class 默认权限为 private

1
2
3
4
5
6
7
8
9
10
11
12
13
//例如:
class C1{
int a;
}
struct C2{
int b;
}
int main(){
C1 c1;
//c1.a = 100;//这行代码是不被允许的,因为class的默认权限为private
C2 c2;
c2.b = 100;//这行代码是被允许的,因为struct的默认权限为public
}

# 构造函数与析构函数

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

C++ 会自动提供构造函数 (空实现) 与析构函数 (空实现) 与拷贝构造函数。

当拥有构造函数 (有参) 时,C++ 将不再自动提供无参空实现,但仍提供拷贝构造函数。

当拥有拷贝构造函数时,C++ 不再自动提供其他构造函数。

构造函数语法:

1. 构造函数没有返回值,也不写 void。

2. 函数名与类名相同。

3. 构造函数可以有参数,因此可以发生重载。

4. 程序在调用对象时会自动调用构造函数,无需手动调用,而且只会调用一次。

== 析构函数语法:==

1. 析构函数没有返回值,也不写 void。

2. 函数名与类名相同,函数名前要添加~

3. 析构函数不可以有参数,因此不能重载。

4. 程序在对象销毁前会自动调用析构函数,无需手动调用,而且只会调用一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//example
#pragma once
#include<iostream>
#include<string>
using namespace std;
class ClassLearn
{
public:
ClassLearn();
~ClassLearn();
int age;
int* height;
};

#include "ClassLearn.h"

ClassLearn::ClassLearn() {
age = 18;
height = new int(175);
cout << "构造函数" << endl;
}

ClassLearn::~ClassLearn() {
if (height != NULL){
delete height;
height = NULL;
}
cout << "析构函数" << endl;
}

拷贝构造函数

1
2
3
4
5
6
7
8
9
10
//example
class Person{
private:
int age;
public:
Person(const Person &p){
//拷贝构造函数,实例化一个类,并将新的实例按照传入的实例复制所有属性
age = p.age;
}
}

# 深拷贝与浅拷贝

浅拷贝:

编译器提供的拷贝构造函数,仅进行浅拷贝操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//例如
class Person(){
public:
int* age;
Person(int age){
this->age = new int(age);
}
~Person(){
if (age != NULL){
delete age;
age = NULL;
}
}
}

int main(){
Person p1(18);
Person p2(p1);
return 0;
}

当其中 p2 对 p1 进行拷贝时,p2 的 int* age 仅仅拷贝了 p1 中 age 的地址。

最后结束时,p2 (栈后进先出) 调用析构函数,释放了 age 指向的堆区内存。

p1 再调用析构函数,则会出现错误。

若要解决浅拷贝的不足,则需重写拷贝构造函数进行深拷贝:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//例如
class Person(){
public:
int* age;
Person(int age){
this->age = new int(age);
}
Person(const Person &p){
this->age = new int(*p.age);
}
~Person(){
if (age != NULL){
delete age;
age = NULL;
}
}
}

int main(){
Person p1(18);
Person p2(p1);
return 0;
}

# 初始化列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//可以通过以下形式,使用构造函数来初始化对象
class Phone{
public:
string m_pName;
Phone(string pName):m_pName(pName){

}
};

class Person{
public:
string name;
int age;
Phone phone;
Person(string name,int age):age(age),name(name){
//当类A的成员中包含类B时,实例化一个类A,会首先实例化类B,再实例化类A。
}
};

# 静态成员

在成员变量或成员函数添加 static 关键字,称为静态成员

静态成员分为:

静态成员变量:1. 所有对象共享一份数据,2. 在编译阶段分配内存,3. 类内声明,类外初始化。

1
2
3
4
5
6
7
8
9
10
//类内声明,类外初始化
class Person{
public://静态成员也有访问权限
static int age;//类内声明
};
int Person::age = 18;//类外初始化
int main(){
//静态成员变量可以通过类名直接访问(如果访问权限是public)
cout << Person::age << endl;
}

静态成员函数:1. 所有对象共享同一个函数,2. 静态成员函数只能访问静态成员变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person{
public://静态成员也有访问权限
static int age;
string name;
static void func(){
cout << "静态成员函数" << endl;
name = "张三";//这个语句是错误的,静态成员函数不能访问非静态成员变量
}
};
int Person::age = 18;
int main(){
//静态成员函数可以通过类名直接访问(如果访问权限是public)
Person::func();
}

# 成员变量与成员函数分开存储

1. 空对象占用 1 个字节。

2. 成员变量占用的空间,即为对象占用的空间。

3. 静态成员变量并不存储在对象上。

4. 成员函数并不存储在对象上。

# this 指针

this 指针无需定义,可直接使用。

this 指针是隐含每一个非静态成员函数内的一种指针。

this 指针指向 被调用的成员函数所属的对象。

用途:

当形参和成员变量同名时,可使用 this 区分。

在类的非静态成员函数中,返回对象本身,可以使用 return *this;

# const 修饰成员函数

成员函数后添加加 const 修饰,被称为常函数

常函数内不可以修改成员属性。

成员属性声明时使用 mutable 修饰,则可以在常函数中修改。

常对象:

声明对象前加 const 修饰,则称为常对象。

常对象只能调用常函数。

# 友元

作用:允许类外一些特殊的函数或类访问一些私有成员。

关键字:friend

1
2
3
4
5
6
7
8
9
10
11
12
13
//写法:
class House{
friend void goodGuy(House* house);//则使用全局函数goodGuy时,就能访问m_BedRoom;
friend class myFriend;//友元类
friend void myFriend::visit();//友元成员函数
private:
string m_BedRoom;
};

class myFriend{
public:
void visit();//成员函数
};

# 运算符重载

# 加号运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//例如
#include<iostream>
using namespace std;

class Person {
public:
int age;
Person(int age = 10):age(age){}
Person operator+ (Person& p) {
Person temp;
temp.age = this->age + p.age;
return temp;
}
};

int main() {
Person p1(18);
Person p2(20);
Person p3 = p1 + p2;
cout << p3.age << endl;
return 0;
}

//上述代码实现了对+运算符的重载,使得main中可以将p1与p2相加。
//也可以使用全局函数实现运算符重载,如下:
Person operator+ (Person &p1, Person &p2){
Person temp;
temp.age = p1.age + p2.age;
return temp;
}
//注:对于内置的数据类型(int之类的)的表达式的运算符是无法改变的
//注2:不要滥用运算符重载

# 左移运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//如下:
class Person {
public:
string m_name;
int m_age;
Person(string name = "张三", int age = 10) :m_name(name), m_age(age) {}
};

ostream& operator<<(ostream& cout, Person& p) {
cout << "m_name=" << p.m_name << "\n" << "m_age=" << p.m_age;
return cout;
}//通常使用全局函数重载左移运算符

int main() {
Person p1("张三", 18);
cout << p1 << endl;
//输出结果为:
//m_name=张三
//m_age=18
return 0;
}

# 递增运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//如下:
#include<iostream>
using namespace std;
class MyInt {
public:
//返回引用,保证一直对一个目标进行操作,便于实现链式操作
MyInt& operator++() {
num++;
return *this;
}
//使用int作为占位符,来区分前置++与后置++。
//此时编译器会将下面这个重载识别为后置++的重载
MyInt operator++(int) {
//注意,这种写法不可以链式后置++(指:(myInt++)++)
MyInt temp(*this);
num++;
return temp;
}
MyInt(int num = 10):num(num){}
int num;
};

ostream& operator<<(ostream& cout, MyInt myInt) {
cout << myInt.num;
return cout;
}

int main() {
MyInt myInt(100);
cout << (myInt++)++ << endl;
cout << myInt << endl;
return 0;
}

# 赋值运算符重载

C++ 编译器提供的赋值运算,仅做浅拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//例如:
class Example{
public:
int* num;
Example(int data){
num = new int(data);
}
~Example(){
if (num != NULL){
delete num;
num = NULL;
}
}
};
int main(){
Example e1(18);
Example e2(20);
e2 = e1;
cout << *e1.num << endl;
cout << *e2.num << endl;
return 0;
}
//因为仅做浅拷贝,所以当e2 = e1时,e2的num仅copy了e1的num存储的地址
//当e1使用完析构之后,num存储的地址被delete。
//则e2再进行delete会导致报错。
//在成员函数中添加如下代码,来重载赋值运算符
Example& operator=(Example& e) {
if (num != NULL) {
delete num;
num = NULL;
}
num = new int(*e.num);
return *this;
}

# 关系运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//例如:
class Person {
public:
string name;
int age;
Person(string name,int age):name(name),age(age){}
bool operator==(Person& p) {
if (this->name == p.name && this->age == p.age)
return true;
return false;
}
bool operator!=(Person& p) {
if (this->name == p.name && this->age == p.age)
return false;
return true;
}
};

int main() {
Person p1("张三", 18);
Person p2("李四", 18);
if (p1 == p2) {
cout << "p1与p2是相等的" << endl;
}
else cout << "p1与p2是不相等的" << endl;
}

# 重载函数运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
//例如
class MyAdd{
public:
//将()重载
int operator()(int a,int b){
return a + b;
}//这种重载方式类似于函数,因此被称为仿函数
};
int main(){
MyAdd myAdd;
cout << myAdd(10, 8) << endl;
return 0;
}

# 继承

# 继承语法与权限

语法:

1
2
3
4
class FatherClass{};
class ChildClass:public FatherClass{

};

继承可以用于减少重复代码,子类可以从父类那里继承相同的代码部分。

继承方式分为三种:

public:公共继承,无法访问 private,继承的成员权限与父类相同

protected:保护继承,无法访问 private,继承的的成员权限变为 protected。

private:私有继承,无法访问 private,继承的成员权限变为 private。

注:继承时仍然继承 private 成员,只是无法访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//实现:
class Enemy {
public:
int hp;
int mp;
};

class Solid :public Enemy {
public:
Solid(int hp, int mp) {
this->hp = hp;
this->mp = mp;
}
void SolidAttack() {
cout << "This is SolidAttack" << endl;
}
};

class Boss :public Enemy {
public:
Boss(int hp, int mp){
this->hp = hp;
this->mp = mp;
}
void BossAttack() {
cout << "This is BossAttack" << endl;
}
};

int main() {
Solid solid(100, 100);
Boss boss(1000, 1000);
solid.SolidAttack();
boss.BossAttack();
}

在实例化一个子类时,会先调用父类的构造函数,再调用子类的构造函数。

在销毁时,会先调用子类的析构函数,再调用父类的析构函数。

多继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//使用如下语法进行多继承
class ChildClass:public Base1, public Base2{
};
//使用多继承时,出现菱形继承会导致成员不明确。
//如:类A,与类B均继承自类Base,现有类C多继承类A与类B,则会导致成员不明确
//1.使用作用域可以访问不同父类的相同成员
//2.使用virtual关键字处理重复数据,如下:
class Base{
int data;
};
class AClass:virtual public Base{};
class BClass:virtual public Base{};
class CClass:public AClass,public BClass{};
//此时CClass中仅会继承1个data,不会再出现不明确。

# 继承时子类与父类的同名成员

当子类中出现与父类中同名的成员时:

1
2
3
4
5
6
7
8
9
10
//对于同名成员:
int main(){
Son s;
cout << "子类成员属性=" << s.element << endl;
cout << "父类成员属性=" << s.Father::element << endl;
cout << "子类成员函数" << s.func() << endl;
cout << "父类成员函数" << s.Father::func() << endl;
//如果子类中出现父类同名的成员函数,子类的同名成员函数会隐藏父类所有的同名成员函数(包括重载)
//如果想调用父类中被隐藏的同名成员函数,需要加作用域
}

对于静态成员,同上。

# 多态

1. 多态分为两类:

  • 静态多态:函数重载和运算符重载属于静态多态,复用函数名。
  • 动态多态:派生类和虚函数实现运行时多态。

2. 静态多态和动态多态的区别:

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//例如:
class Animal {
public:
//添加virtual关键字,使函数地址晚绑定
//从而实现speak函数发出小猫在说话
virtual void speak() {
cout << "动物在说话" << endl;
}
};

class Cat:public Animal {
public:
void speak() {
cout << "小猫在说话" << endl;
}
};

void speak(Animal& animal) {
animal.speak();
}

int main() {
Cat cat;
speak(cat);
}

多态满足条件:

  • 有继承关系
  • 子类重写父类中的虚函数

多态使用条件:

  • 父类指针或引用指向子类对象

重写:函数返回值类型、函数名、参数列表完全一致称为重写

# 纯虚函数与抽象类

纯虚函数:使用 virtual 关键字修饰,并且无实现的类成员函数。

语法:virtual void func () = 0;

抽象类:只要其中有一个纯虚函数,则这个类称为抽象类。

1. 抽象类无法实例化。

2. 抽象类的子类必须重写纯虚函数,否则该子类也为抽象类。

# 虚析构与纯虚析构

当使用父类指针指向子类,若子类中存有堆区数据,则结束时,仅调用父类的析构函数可能会无法释放子类的堆区数据,从而导致内存泄漏。

解决方法:将父类的析构函数改为虚析构函数。

纯虚析构:纯虚析构需要声明,也需要实现。

# 案例:Computer 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//头文件
#pragma once
#include<iostream>
#include<string>
using namespace std;
class CPU {
public:
virtual void calculate() = 0;
};
class Memory {
public:
virtual void storage() = 0;
};
class GPU {
public:
virtual void display() = 0;
};
class IntelCPU :public CPU {
public:
void calculate();
};
class IntelGPU :public GPU {
public:
void display();
};
class AMDCPU :public CPU {
public:
void calculate();
};
class AMDGPU :public GPU {
public:
void display();
};
class ZQMemory :public Memory {
public:
void storage();
};
class MGMemory :public Memory {
public:
void storage();
};
class Computer {
public:
Computer(CPU* cpu, Memory* memory, GPU* gpu);
~Computer();
void work();
private:
CPU* m_cpu;
Memory* m_memory;
GPU* m_gpu;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//源文件
#include "Computer.h"
Computer::Computer(CPU* cpu, Memory* memory, GPU* gpu) {
m_cpu = cpu;
m_memory = memory;
m_gpu = gpu;
}
Computer::~Computer() {
if (m_cpu != NULL) {
delete m_cpu;
m_cpu = NULL;
}
if (m_memory != NULL) {
delete m_memory;
m_memory = NULL;
}
if (m_gpu != NULL) {
delete m_gpu;
m_gpu = NULL;
}
}
void Computer::work() {
m_cpu->calculate();
m_memory->storage();
m_gpu->display();
}
void IntelCPU::calculate() {
cout << "IntelCPU正在计算" << endl;
}
void IntelGPU::display() {
cout << "IntelGPU正在显示" << endl;
}
void AMDCPU::calculate() {
cout << "AMDCPU正在计算" << endl;
}
void AMDGPU::display() {
cout << "AMDGPU正在显示" << endl;
}
void ZQMemory::storage() {
cout << "芝奇内存条正在存储" << endl;
}
void MGMemory::storage() {
cout << "镁光内存条正在存储" << endl;
}
1
2
3
4
5
6
7
8
9
10
//主文件
#include<iostream>
#include"Computer.h"
using namespace std;

int main() {
Computer computer(new IntelCPU, new ZQMemory, new AMDGPU);
computer.work();
return 0;
}

# 模板

C++ 另一种编程思想称为泛型编程,主要利用的技术就是模板

C++ 提供两种模板机制:函数模板类模板

# 调用规则

1. 如果函数模板和普通函数都可以实现,优先调用普通函数。

2. 可以通过空模板参数列表来强制调用函数模板。

3. 函数模板也可以发生重载。

4. 如果函数模板可以产生更好的匹配,优先调用函数模板。

# 函数模板语法

1
2
3
4
5
template<typename T>
函数声明或定义
//template 声明创建模板
//typename 表面其后面的符号是一种数据类型,可以用class代替
//T 通用的数据类型,名称可以替换,通常为大写字母

案例 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//例如
include<iostream>
using namespace std;
template<typename T>
void swapNum(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}

int main() {
int a = 10, b = 20;

//使用方式:
//1.自动类型推导
//swapNum(a, b);
//2.显示指定类型
swapNum<int>(a, b);
cout << "a=" << a << "\tb=" << b << endl;
}

案例 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<iostream>
using namespace std;
template<class T>//选择排序模板
void selectionSort(T arr[],int len) {
for (int i = 0; i < len; i++) {
int max = i;
for (int j = i + 1; j < len; j++) {
if (arr[max] < arr[j]) {
max = j;
}
}
if (max != i) {
T temp = arr[i];
arr[i] = arr[max];
arr[max] = temp;
}
}
}

template<class T>//打印模板
void print(T arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i];
}
cout << endl;
}

void test01() {//字符数组排序
char charArr[] = "badcfe";
int len = sizeof(charArr) / sizeof(charArr[0]);
selectionSort(charArr, len);
print(charArr, len);
}
void test02() {//整型数组排序
int intArr[] = {6,8,2,1,4,6,3,8,7,9};
int len = sizeof(intArr) / sizeof(intArr[0]);
selectionSort(intArr, len);
print(intArr, len);
}

int main() {
test01();
test02();
system("pause");
}

# 类模板语法

1
2
template<class T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//例如
template<class NameType, class AgeType = int>
//类模板的模板参数列表中可以有默认参数
class Person {
public:
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson();

NameType m_Name;
AgeType m_Age;
};
template<class T1, class T2>//类外实现成员函数
void Person<T1, T2>::showPerson() {
cout << "Person_Name:" << this->m_Name << "\tPerson_Age:" << this->m_Age << endl;
}

void test() {
//使用类模板创建类时,不能自动类型推导
Person<string, int> p1("Michael", 18);
p1.showPerson();;
}

int main() {
test();
system("pause");
return 0;
}

# 模板重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//例如
class Person {
public:
Person(string name, int age) :m_Name(name), m_Age(age) {};
string m_Name;
int m_Age;
};

template<class T>//此模板不能用于Person类的比较,需要重载
bool myCompare(T& a, T& b) {
if (a == b)
return true;
else return false;
}

template<> bool myCompare(Person& p1, Person& p2) {//重载格式
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
return true;
else return false;
}

void test() {
Person p1("Michael", 18);
Person p2("Michael", 18);
if (myCompare(p1, p2))
cout << "p1 == p2" << endl;
}

int main() {
test();
system("pause");
return 0;
}

# 类模板与继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//若父类为一个类模板,则子类需要指出父类中的数据类型
//若要子类所需的类型也为灵活类型,则子类也需要是类模板
//例如
template<class AgeType>
class Base {//模板基类
public:
AgeType base_Age;
};

class C1 :public Base<int> {//非模板派生类
public:
C1(int age) {
this->base_Age = age;
this->c1_Age = age;
}
int c1_Age;
};

template<class AgeType>//模板派生类
class C2 :public Base<AgeType> {
public:
C2(AgeType age) {
this->base_Age = age;
this->c2_Age = age;
}
AgeType c2_Age;
};

void test() {
C1 c1(18);
C2<int> c2(20);
cout << "C1:" << "\tbase_Age:" << c1.base_Age << "\tc1_Age:" << c1.c1_Age << endl;
cout << "C2:" << "\tbase_Age:" << c2.base_Age << "\tc2_Age:" << c2.c2_Age << endl;
}

int main() {
test();
system("pause");
return 0;
}

# 类模板案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//数组类封装
#pragma once
#include<iostream>
#include<string>
using namespace std;

template<class DataType>
class MyArray {
private:
DataType* pAddress;//数组地址
int m_Count;//元素个数
int m_Size;//数组容量
public:
MyArray(int Size) {
this->m_Count = 0;
this->m_Size = Size;
this->pAddress = new DataType[this->m_Size];
}
~MyArray() {
if (pAddress != NULL) {
delete[] this->pAddress;
this->pAddress = NULL;
}
}
//拷贝构造,防止浅拷贝
MyArray(const MyArray& arr) {
this->m_Count = arr.m_Count;
this->m_Size = arr.m_Size;
//深拷贝
this->pAddress = new DataType[arr.m_Size];
//将arr中的数据都拷贝过来
for (int i = 0; i < this->m_Count; i++) {
this->pAddress[i] = arr.pAddress[i];
}
}
//operator= 防止浅拷贝问题
MyArray& operator=(const MyArray& arr) {
//先判断原来堆区是否有数据,如果有,先释放
if (this->pAddress != NULL) {
delete[] this->pAddress;
this->pAddress = NULL;
this->m_Count = 0;
this->m_Size = 0;
}
this->m_Count = arr.m_Count;
this->m_Size = arr.m_Size;
//深拷贝
this->pAddress = new DataType[arr.m_Size];
//将arr中的数据都拷贝过来
for (int i = 0; i < this->m_Count; i++) {
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}

//尾插法
void Push_Back(const DataType& val) {
if (this->m_Count == this->m_Size) {
cout << "Array Full!" << endl;
return;
}
this->pAddress[this->m_Count] = val;
this->m_Count++;
}

//尾删法
void Pop_Back() {
if (this->m_Count == 0) {
return;
}
this->m_Count--;
}

//通过下标方式访问数组中的元素,即重载[]运算符
DataType& operator[](int index) {
if (index > this->m_Count - 1) {
this->m_Count++;
}
return this->pAddress[index];
}

//返回数组大小
int getSize() {
return this->m_Size;
}

//返回数组内元素数
int getCount() {
return this->m_Count;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//main
#include<iostream>
#include<string>
#include"MyArray.hpp"
using namespace std;

class Person {
public:
Person() {};
Person(string name, int age) :m_name(name), m_age(age) {};
void show() {
cout << "name: " << this->m_name << "\tage: " << this->m_age << endl;
}

string m_name;
int m_age;
};

void test() {
//MyArray测试代码
MyArray<int> arr(5);
for (int i = 0; i < arr.getSize(); i++) {
arr[i] = i;
}
for (int i = 0; i < arr.getSize(); i++) {
cout << arr[i] << endl;
}
cout << arr.getCount() << "\t" << arr.getSize() << endl;
arr.Pop_Back();
cout << arr.getCount() << "\t" << arr.getSize() << endl;
arr.Push_Back(500);
cout << arr.getCount() << "\t" << arr.getSize() << endl;

//自定义类型测试
MyArray<Person> pArr(3);
Person p1("Michael", 18);
Person p2("zhangsan", 19);
Person p3("lisi", 20);
pArr[0] = p1;
pArr[1] = p2;
pArr[2] = p3;
for (int i = 0; i < pArr.getCount(); i++) {
pArr[i].show();
}
}

int main() {
test();
system("pause");
return 0;
}

# STL

# STL 基本概念

STL(Standard Template Library)

STL 从广义上分为:容器 (container)、算法 (algorithm)、迭代器 (iterator)

迭代器容器算法的桥梁

STL 几乎所有代码都采用了模板类,或者模板函数

STL 六大组件:容器、算法、迭代器、仿函数、适配器 (配接器)、空间配置器

  • 容器:各种数据结构,如 vector、list、deque、set、map 等,用于存放数据

  • 算法:各种常用算法,如 sort、find、copy、for_each 等

  • 迭代器:扮演了容器与算法之间的胶合剂

  • 仿函数:行为类似函数,可作为算法的某种策略

  • 适配器:一种用来修饰容器、仿函数或者迭代器接口的东西

  • 空间配置器:负责空间的配置与管理

# Vector 容器

# Vector 容器语法

C++ STL 中的 verctor 好比是 C 语言中的数组,但是 vector 又具有数组没有的一些高级功能。与数组相比,vector 就是一个可以不用再初始化就必须制定大小的边长数组,当然了,它还有许多高级功能。

Vector 也称为单端数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<iostream>
#include<vector>
using namespace std;

int main(){
vector<type> vectorName;//创建一个vector
//方法
push_back(element);//尾插
vector[i];//访问第i个元素,注意,添加元素需要使用push方法。此形式只可用于读取与修改
begin();//返回头
end();//返回尾
size();//返回vector的元素数
resize(val);//重新指定vector内元素数,若源vector超出,则截断,若不足,则补0
capacity();//返回vector容器的容量
clear();//清空vector
reserve(val);//预留内存空间,可以减少容器扩展次数

insert();//插入元素、四种重载:
//1. vector.insert(vector.begin(), value);//在头部插入value
//2. vector.insert(vector.end(), number, value);//在尾部插入number个value
//3. vector.insert(vector.end(), vector2.begin(), vector2.end());//在vector尾部插入整个vector2
//4. vector.insert(vector.end(), { value1, value2});//在其尾部插入value1与value2

pop_back();//删除尾部元素
erase(element_it);//传入element的迭代器it,将element删除,并返回其下一个元素的迭代器
swap(vec);//将vec与源vector互换,可用于收缩内存空间

assign();//两种情况:1. assign(n,val)将vector中元素变为n个val
//2. vectorName.assign(vector.begin(),vector.end())将vector中的元素赋值给vectorName
}

//vector容器的迭代器是支持随机访问的迭代器

# Vector 容器的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//例1	存储基本数据类型
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << endl;
}

void test() {
//创建一个vector容器
vector<int> v;

//向容器中插入数据
for (int i = 0; i < 100; i += 10) {
v.push_back(i);
}

////通过迭代器访问容器中的数据
//vector<int>::iterator itBegin = v.begin();//起始迭代器,指向vector容器中的最后一个元素
//vector<int>::iterator itEnd = v.end();//结束迭代器,指向vector容器最后一个元素的下一个位置

////遍历方式1
//while (itBegin != itEnd) {
// cout << *itBegin << endl;
// itBegin++;
//}

////遍历方式2
//for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
// cout << *it << endl;
//}

//遍历方式3,引入algorithm头文件,使用STL提供的遍历算法
for_each(v.begin(), v.end(), myPrint);//回调函数
}

int main() {
test();
system("pause");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//例2	存储自定义数据类型
#include<iostream>
#include<string>
#include<vector>
using namespace std;

class Person {
public:
string m_name;
int m_age;
Person(string name, int age) :m_name(name), m_age(age) {};
};

void test(){
vector<Person> v;
string nameSeed[] = { "张三","李四","王五" };
for (int i = 0; i < 3; i++) {
Person p(nameSeed[i], 18);
v.push_back(p);
}
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
cout << "name:" << it->m_name << "\tage:" << it->m_age << endl;
}
}

int main() {
test();
system("pause");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//例3	容器嵌套
void test(){
vector< vector<int> > vBase;
vector<int> v1;
vector<int> v2;
vector<int> v3;

for (int i = 0; i < 3; i++) {
v1.push_back(i);
v2.push_back(i);
v3.push_back(i);
}

vBase.push_back(v1);
vBase.push_back(v2);
vBase.push_back(v3);

for (vector<vector<int>>::iterator itBase = vBase.begin(); itBase != vBase.end(); itBase++) {
for (vector<int>::iterator it = itBase->begin(); it != itBase->end(); it++) {
cout << *it << endl;
}
}
}

int main() {
test();
system("pause");
return 0;
}

# String 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//基础功能:拼接之类的不再介绍
//string查找、替换与比较
#include<iostream>
#include<string>
using namespace std;

//查找
void test01() {
string str1 = "abcdefg";
int pos = str1.find("de");//find从左向右查找
//rfind从右向左查找
if (pos == -1) {//若未找到值,则会返回-1
cout << "value is not found" << endl;
}
else {
cout << "value's index is " << pos << endl;
}
}

//替换
void test02(){
string str1 = "abcdefg";
str1.replace(1, 3, "1111");//从下标为1的位置,将3个字符替换,替换为1111
//输出结果为 a1111efg
cout << "str1=" << str1 << endl;
}

//比较
void test03() {
string str1 = "hello";//compare函数会逐字符比对其ASCII码
string str2 = "hello";
string str3 = "xello";
if (str1.compare(str2) == 0) {//如果str1==str2则返回0
cout << "str1 == str2" << endl;
}
if (str1.compare(str3) < 0) {
cout << "str1 < str3" << endl;
}
//如果str1更大,则返回值大于0
//如果str2更大,则返回值小于0
}

//字符存取
void test04() {
string str = "Hello World";
for (int i = 0; i < str.size() - 1; i++) {
str[i] = str.at(i + 1);
cout << str.at(i);
}
cout << endl;
}

//插入删除
void test05() {
string str = "hello";
//在下标为1的位置插入111
str.insert(1, "111");
cout << str << endl;//输出结果为h111ello

str.erase(1, 3);//从下标为1的位置开始,删除3个字符
cout << str << endl;//输出结果为hello
}

//截取复制字符串
void test06() {
string email = "[email protected]";
int pos = email.find("@");
//从0开始截取,到pos的位置
string usrName = email.substr(0, pos);
//substr不破坏源字符串
cout << usrName << endl;
}

int main() {
test01();
test02();
test03();
test04();
test05();
test06();
system("pause");
return 0;
}

# Deque 容器

  • vector 对于头部的插入、删除效率低,数据量越大,效率越低
  • deque 相对而言,对头部的插入、删除速度会比 vector 快
  • vector 访问元素时的速度会比 deque 快

# Deque 容器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream>
#include<deque>
using namespace std;
int main(){
deque<type> dequeName;
//下面为deque的方法
push_back();//尾插
push_front();//头插
pop_back();//尾删
pop_front();//头删

insert();//参考vector

front();//获得队列头部元素。
back();//获得队列尾部元素。
size();//获得队列元素数。
resize();//参考vector

empty();//参考vector
assign();//参考vector

sort(iterator begin,iterator end);//排序

clear();//清空
return 0;
}
//deque容器的迭代器也是支持随机访问的迭代器

# Stack 容器

# Stack 容器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stack>
#include<iostream>
using namespace std;

int main(){
stack<type> stackName;//创建一个stackName栈
stackName.empty();//如果栈为空,则返回true,否则返回false
stackName.size();//返回栈中元素的个数
stackName.top();//返回栈顶的元素,但不删除该元素
stackName.pop();//弹出栈顶元素,但不返回该元素
stackName.push();//将元素入栈
return 0;
}

# Queue 容器

# Queue 容器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//C++提供的队列位于头文件<queue>中
#include<iostream>
#include<queue>
using namespace std;
int main(){
queue<type> queueName;//创建一个queueName队列
queueName.empty();//如果队列为空,返回true,否则返回false
queueName.size();//返回队列中元素的个数
queueName.pop();//删除队列首元素但不返回其值
queueName.front();//返回队首元素的值,但不删除该元素
queueName.push();//在队尾压入新元素
queueName.back();//返回队列尾元素的值,但不删除该元素
return 0;
}

# list 容器

list 翻译为列表,C++ 中的列表相当于链表 (Link List),包含数据域和指针域

STL 提供的链表是双向循环链表

# list 容器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include<list>
using namespace std;
int main(){
list<type> listName;//创建一个列表
//下面为列表的方法
begin();//返回表头
end();//返回表尾的后一个,实际为空,不能访问,常作为循环结束条件
push_front();//从表头插入元素
push_back();//从表尾插入元素
empty();//判断列表是否为空,若空则返回true,否则返回false
resize();//修改列表的长度,超出长度的元素将被删除,若不足,则以0填充
clear();//清空列表的所有元素
front();//返回表头元素
back();//返回表尾元素
pop_front();//删除表头元素
pop_back();//删除表尾元素

assign();//两种情况:1. assign(n,val)将列表中元素变为n个val
//2. listName.assign(list.begin(),list.end())将list中的元素赋值给listName
swap();//两种写法:1. list1.swap(list2) 2. swap(list1,list2) 作用:交换两个列表
reverse();//逆置列表

merge();//合并两个列表并排序(默认升序).
//使用:list1.merge(list2,greater<int>());调用后将list2的元素合并到list1,并清空list2,同时对list1排序
//排序方式默认为升序,其中greater<int>()可省略,排序方式可修改

insert();//间插,在指定位置插入一个或多个元素.
//使用:list1.insert(index,number,val);在index位置插入number个val,其中number可省略,省略则为插入1个
//特别:list1.insert(list1.begin(),list2.begin(),list2.end());在list1的头部插入list2的所有元素

erase();//删除一个元素,或一个区域的元素
//1. list1.(index);将list1第index个元素删除
//2. list1.(index1,index2);将list1种第index1到第index2之间的元素删除

sort();//链表排序

return 0;
}
//list提供的迭代器是双向迭代器,而非随机访问的迭代器

# set/multiset 容器

set/multiset 容器又称为集合容器

元素插入容器时,会自动被排序。

set/multiset 属于关联式容器,底层结构是用二叉树实现的

set 和 multiset 区别:

  • set 不允许容器中有重复元素
  • multiset 允许容器中有重复元素

# set/multiset 容器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
#include<set>
using namespace std;
int main(){
set<Type> setName;

size();//返回容器中元素的数目
empty();//判断容器是否为空
swap(s2);//交换两个集合容器

insert(val);//插入元素
clear();//清空
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(val);//删除容器中值为val的元素

find(key);//查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,则返回end();
count(key);//统计key的元素个数
}

# set 容器排序

set 容器默认排序规则为从升序,可以使用仿函数改变 set 容器的排序规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//1.基础数据类型排序规则改变
//例如
#include<iostream>
#include<set>
using namespace std;

class MySort {
public:
bool operator()(int v1, int v2) const {
return v1 > v2;
}
};

void test() {
set<int, MySort> s;

s.insert(10);
s.insert(20);
s.insert(30);

for (set<int, MySort>::iterator it = s.begin(); it != s.end(); it++) {
cout << *it << "\t";
}
}

int main() {
test();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//2.自定义数据类型排序规则改变
//例如
#include<iostream>
#include<set>
using namespace std;

class Person {
public:
Person(string name, int age) {
this->m_name = name;
this->age = age;
}
string m_name;
int age;
};

class ComparePerson {
public:
bool operator()(const Person& p1, const Person& p2) const {
return p1.age > p2.age;
}
};

void test() {
//自定义数据类型通常会指定排序规则
set<Person, ComparePerson> s;

Person p1("Michael", 18);
Person p2("John", 19);
Person p3("Mike", 20);

s.insert(p1);
s.insert(p2);
s.insert(p3);

for (set<Person, ComparePerson>::iterator it = s.begin(); it != s.end(); it++) {
cout << "name:" << it->m_name << "\tage:" << it->age << endl;
}
}

int main() {
test();
return 0;
}

# pair (对组)

# pair 语法

1
2
3
4
5
6
7
8
9
10
11
12
//pair(对组)是成对出现的数据,pair不需要包含头文件
//语法
#include<iostream>
using namespace std;
int main(){
//创建方式1
pair<type1, type2> pairName(type1Data, type2Data);

//创建方式2
pair<type1, type2> pairName = make_pair(type1Data, type2Data);
return 0;
}

# pair 与 set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//使用set进行插入时,会返回一个pair
//例如:
#include<iostream>
#include<set>
using namespace std;
void test() {
set<int> s;
s.insert(10);
pair<set<int>::iterator,bool> ret = s.insert(10);
if (ret.second) {//使用first访问迭代器,使用second访问bool值
cout << "插入成功" << endl;//如果插入成功,则会返回true
}
else {
cout << "插入失败" << endl;
}
}

int main() {
test();
return 0;
}

# map/multimap 容器

# 简介:

  • map 中所有元素都是 pair
  • pair 中第一个元素为 key (键值),起到索引作用,第二个元素为 value (实值)
  • 所有元素都会根据元素的 key (键值) 自动排序

# 本质:

  • map/multimap 属于关联式容器,底层结构使用二叉树实现

# 优点:

  • 可以根据 key 值快速找到 value 值

# map 和 multimap 区别:

  • map 不允许容器中有重复的 key
  • multimap 允许容器中有重复的 key

# map/multimap 容器语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//构造
map<T1, T2> mapNmae;//默认构造
map2(map);//拷贝构造
map3 = map2;//赋值构造
//例如
#include<iostream>
#include<map>
using namespace std;

void printMap(map<int, int>& m) {
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key:" << it->first << "\tvalue:" << it->second << endl;
}
}

void test() {
map<int, int> m;

m.insert(pair<int, int>(2, 20));//插入方法1
m.insert(make_pair(1, 10));//插入方法2
m.insert(map<int, int>::value_type(3, 30));//插入方法3
m[4] = 40;//插入方法4(不推荐使用)
//若检索的key不存在,则该方法会自动创建一个元素,并将其value赋为0
//若插入时写错key,会导致map容器改变

printMap(m);
}

int main() {
test();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//方法
int main(){
map<keyType, valueType> mapName;

size();//返回元素个数
empty();//判断容器是否为空
swap(map);//交换两个集合容器

insert(elem);//插入元素,有多种插入方法,可以参考上面的构造案例
clear();//清空
erase(pos);//删除pos迭代器所指元素,返回下一个元素的迭代器
erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key);//删除key值为key的元素

find(key);//查找key是否存在,若存在,返回其迭代器,若无,返回end()
count(key);//统计key值为key的元素个数

}

# map 容器排序规则重载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//使用伪函数重载map排序规则
#include<iostream>
#include<map>
using namespace std;

class MyCompare {
public:
bool operator()(int v1, int v2) const {
return v1 > v2;
}
};

void printMap(map<int, int, MyCompare>& m) {
for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key:" << it->first << "\tvalue:" << it->second << endl;
}
}

void test() {
map<int, int, MyCompare> m;

m.insert(pair<int, int>(2, 20));
m.insert(make_pair(1, 10));
m.insert(map<int, int>::value_type(3, 30));

printMap(m);
}

int main() {
test();
return 0;
}

# 迭代器

要访问顺序容器和关联容器中的元素,需要通过 “迭代器(iterator)” 进行。

迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。

迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//迭代器按照定义方式分成以下四种。

//1) 正向迭代器,定义方法如下:
//容器类名::iterator 迭代器名;


//2) 常量正向迭代器,定义方法如下:
//容器类名::const_iterator 迭代器名;


//3) 反向迭代器,定义方法如下:
//容器类名::reverse_iterator 迭代器名;


//4) 常量反向迭代器,定义方法如下:
//容器类名::const_reverse_iterator 迭代器名;

/*
通过迭代器可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素。

迭代器都可以进行++操作。反向迭代器和正向迭代器的区别在于:
对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素;
而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。
*/

# STL - 函数对象 (仿函数)

# 仿函数概念

重载函数调用操作符的类,其对象常称为函数对象

函数对象使用重载的 () 时,行为类似函数调用,也叫仿函数

** 本质:** 函数对象 (仿函数) 是一个类,而不是一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//案例
#include<iostream>
using namespace std;

class MyAdd {
public:
MyAdd() {
this->count = 0;
}
int operator()(int v1, int v2) {
this->count++;
return v1 + v2;
}

int count;
};

void test() {
MyAdd myAdd;
cout << myAdd(10, 20) << endl;
cout << myAdd.count << endl;
}

int main() {
test();
system("pause");
return 0;
}

# 谓词

返回 bool 类型的仿函数称为谓词

如果 operator () 接受一个参数,叫做一元谓词

如果 operator () 接收两个参数,叫做二元谓词

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

//一元谓词
class GreaterFive {
public://查找大于5的数
bool operator()(int val) {
return val > 5;
}
};

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
//algorithm中的查找算法,按照条件查找,会返回一个迭代器
vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());//第三个参数 _Pr _Pred 即需要一个谓词
cout << *it << endl;
}

//二元谓词
class MyCompare {
public://降序排列
bool operator()(int v1,int v2) {
return v1 > v2;
}
};

void test02() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(40);
v.push_back(50);
v.push_back(30);

sort(v.begin(), v.end(), MyCompare());

for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << "\t";
}

}

int main() {
test01();
test02();
system("pause");
return 0;
}

# STL 内建函数对象

使用内建函数对象时,需要引入头文件 <functional>

# 算术仿函数

1
2
3
4
5
6
7
8
//原型
template<class T> T plus<T> //加法仿函数
template<class T> T minus<T> //减法
template<class T> T multiplies<T> //乘法
template<class T> T divides<T> //除法
template<class T> T modulus<T> //求余
template<class T> T negate<T> //取反
//其中只有negate是一元运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//使用案例
#include<iostream>
#include<functional>
using namespace std;
void test(){
negate<int> n;
cout << n(50) << endl;

plus<int> p;
cout << p(10, 20) << endl;
}

int main() {
test();
system("pause");
return 0;
}

# 关系仿函数

1
2
3
4
5
6
7
//原型
template<class T> bool equal_to<T> //等于
template<class T> bool not_equal_to<T> //不等于
template<class T> bool greater<T> //大于
template<class T> bool greater_equal<T> //大于等于
template<class T> bool less<T> //小于
template<class T> bool less_equal<T> //小于等于
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
#define COUNT 5

void test(){
vector<int> v;
for ( int i = 0; i < COUNT; i++) {
v.push_back(rand() % 500);
}
sort(v.begin(), v.end(), greater<int>());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << "\t";
}
}

int main() {
test();
return 0;
}

# 逻辑仿函数

1
2
3
4
//原型
template<class T> bool logical_and<T> //逻辑与
template<class T> bool logical_or<T> //逻辑或
template<class T> bool logical_not<T> //逻辑非
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;

void test() {
vector<bool> v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << "\t";
}
cout << endl;

//利用逻辑非 将容器v 搬运到容器v2中,并执行取反操作
vector<bool> v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());

for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++) {
cout << *it << "\t";
}
}

int main() {
test();
return 0;
}

# STL 常用算法

# for_each

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//使用案例
//语法:for_each(遍历起始位置,遍历终止位置,回调函数)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void print(int val) {
cout << val << "\t";
}

class MyPrint {
public:
void operator()(int val) {
cout << val << "\t";
}
};

void test() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
//for_each(v.begin(), v.end(), print);
//或者使用仿函数
for_each(v.begin(), v.end(), MyPrint());
}

int main() {
test();
return 0;
}

# transform

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//使用案例
//语法:transform(copy源起始位置,copy源终止位置,目标起始位置,copy规则)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void print(int val) {
cout << val << "\t";
}

class MyPrint {
public:
int operator()(int val) {
return val + 1;//对操作数+1,若不改变,可直接返回原值
}
};

void test() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}

vector<int> vTarget;
vTarget.resize(v.size());//开辟空间,便于目标容器接收
transform(v.begin(), v.end(), vTarget.begin(), MyPrint());

for_each(vTarget.begin(), vTarget.end(), print);
}

int main() {
test();
return 0;
}

# 查找算法

1
2
3
4
5
6
7
//简介
find; //查找元素
find_if; //按条件查找元素
adjacent_find; //查找相邻重复元素
binar_search; //二分查找(仅适用于升序)
count; //统计元素个数
count_if; //按条件统计元素个数

# find:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class Person {
public:
Person(string name,int age) {
this->m_name = name;
this->age = age;
}

//重载 == 使底层find知道如何对比Person数据类型
bool operator== (const Person& p) {
if (this->m_name == p.m_name && this->age == p.age)
return true;
return false;
}
string m_name;
int age;
};

//基础数据类型
void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}

//查找容器中是否存在 5 这个元素,若存在,返回其迭代器,若不存在,返回end()
vector<int>::iterator it = find(v.begin(), v.end(), 5);
if (it == v.end())
cout << "not found";
else
cout << *it << "存在";
}

//自定义数据类型
void test02() {
vector<Person> v;
v.push_back(Person("aaa", 10));
v.push_back(Person("bbb", 20));
v.push_back(Person("ccc", 30));
v.push_back(Person("ddd", 40));

vector<Person>::iterator it = find(v.begin(), v.end(), Person("bbb", 20));
if (it == v.end())
cout << "not found";
else
cout << it->m_name << "\t" << it->age;
}

int main() {
//test01();
test02();
return 0;
}

# find_if:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class GreaterFive {
public:
bool operator()(int val) {
return val > 5;
}
};

class Person {
public:
Person(string name, int age) {
this->m_name = name;
this->age = age;
}

string m_name;
int age;
};

class Greater20 {
public:
bool operator()(Person& p) {
if (p.age > 20)
return true;
return false;
}
};

//基础数据类型
void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}

vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end())
cout << "not found";
else
cout << *it;
cout << endl;
}

//自定义数据类型
void test02() {
vector<Person> v;
v.push_back(Person("aaa", 10));
v.push_back(Person("bbb", 20));
v.push_back(Person("ccc", 30));
v.push_back(Person("ddd", 40));

vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20());
if (it == v.end())
cout << "not found";
else
cout << it->m_name << "\t" << it->age;
}

int main() {
test01();
test02();
return 0;
}

# adjacent_find:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

void test01() {
vector<int> v;
v.push_back(10);
v.push_back(120);
v.push_back(103);
v.push_back(10);//会返回这个元素的迭代器
v.push_back(10);
v.push_back(103);

vector<int>::iterator it = adjacent_find(v.begin(), v.end());
if (it == v.end())
cout << "not found";
else
cout << *it;
cout << endl;
}

int main() {
test01();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//注意:二分查找法返回的并非迭代器,而是布尔值
//注意2:STL提供的二分查找法仅适用于 升序排列
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}

//查找元素9
bool ret = binary_search(v.begin(), v.end(), 9);
if (ret)
cout << "9存在";
else
cout << "9不存在";
}

int main() {
test01();
return 0;
}

# count:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class Person {
public:
Person(string name, int age) {
this->m_name = name;
this->age = age;
}
bool operator== (const Person& p) {
if (this->m_name == p.m_name && this->age == p.age)
return true;
return false;
}
string m_name;
int age;
};

//基础数据类型
void test01() {
vector<int> v;
v.reserve(22);
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
for (int i = 0; i < 5; i++) {
v.push_back(i);
}
for (int i = 0; i < 7; i++) {
v.push_back(i);
}

int num = count(v.begin(), v.end(), 6);
cout << "6的个数为:" << num << endl;
}

//自定义数据类型
void test02() {
vector<Person> v;
v.push_back(Person("aaa", 10));
v.push_back(Person("bbb", 20));
v.push_back(Person("aaa", 10));
v.push_back(Person("ccc", 40));
v.push_back(Person("aaa", 30));

Person p("aaa", 10);

int num = count(v.begin(), v.end(), p);
cout << "与p相同的人共有:" << num << "个" << endl;
}

int main() {
test01();
test02();
return 0;
}

# count_if:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class GreaterFive {
public:
bool operator()(const int& val) {
return val > 5;
}
};

class Person {
public:
Person(string name, int age) {
this->m_name = name;
this->age = age;
}
bool operator== (const Person& p) {
if (this->m_name == p.m_name && this->age == p.age)
return true;
return false;
}
string m_name;
int age;
};

class AgeGreater20 {
public:
bool operator()(const Person& p) {
if (p.age > 20)
return true;
return false;
}
};

//基础数据类型
void test01() {
vector<int> v;
v.reserve(22);
for (int i = 0; i < 10; i++) {//4个
v.push_back(i);
}
for (int i = 0; i < 5; i++) {
v.push_back(i);
}
for (int i = 0; i < 7; i++) {//1个
v.push_back(i);
}

int num = count_if(v.begin(), v.end(), GreaterFive());
cout << "大于5的元素的个数为:" << num << endl;
}

//自定义数据类型
void test02() {
vector<Person> v;
v.push_back(Person("aaa", 10));
v.push_back(Person("bbb", 20));
v.push_back(Person("ccc", 30));
v.push_back(Person("ddd", 40));
v.push_back(Person("eee", 50));

int num = count_if(v.begin(), v.end(), AgeGreater20());
cout << "年龄大于20的人共有:" << num << "个" << endl;
}

int main() {
test01();
test02();
return 0;
}

# 排序算法

1
2
3
4
5
//简介
sort(itBegin, itEnd, _Pred); //对容器内元素进行排序
random_shuffle(itBegin, itEnd); //洗牌 指定范围内的元素随机调整次序
merge(itBeg1, itEnd1, itBeg2, itEnd2, itTarget);//有序容器元素合并,并存储到另一容器中
reverse(itBeg, itEnd); //反转指定范围的元素

# sort:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<functional>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {//4个
v.push_back(i);
}
for (int i = 0; i < 5; i++) {
v.push_back(i);
}
for (int i = 0; i < 7; i++) {//1个
v.push_back(i);
}
//默认升序
sort(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
//降序
sort(v.begin(), v.end(), greater<int>());
for_each(v.begin(), v.end(), myPrint);
}

int main() {
test01();
return 0;
}

# random_shuffle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
#include<ctime>
#include<cstdlib>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
//注意,若要每次都随机需要使用srand
random_shuffle(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
}

int main() {
srand((unsigned)time(NULL));
test01();
return 0;
}

merge:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//注意,容器1与2必须是有序的,且排序方式相同
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
v2.push_back(i + 1);
}

//目标容器
vector<int> vTarget;
vTarget.resize(v1.size() + v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), vTarget.end(), myPrint);
}

int main() {
test01();
return 0;
}

# reverse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
cout << "反转前:" << endl;
for_each(v.begin(), v.end(), myPrint);

reverse(v.begin(), v.end());
cout << "反转后:" << endl;
for_each(v.begin(), v.end(), myPrint);
}

int main() {
test01();
return 0;
}

# 拷贝与替换算法

1
2
3
4
5
//简介
copy(itBeg, itEnd, itTargetBeg); //容器内指定范围的元素拷贝到另一容器
replace(itBeg, itEnd, oldVal, newVal); //将容器内指定范围的旧元素修改为新元素
replace_if(itBeg, itEnd, _Pred, newVal);//容器内指定范围,满足条件的元素替换为新元素
swap(container c1, container c2); //互换两个容器的元素

# copy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}

vector<int> v2;
v2.resize(v1.size());
copy(v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), v2.end(), myPrint);
}

int main() {
test01();
return 0;
}

# replace:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), myPrint);
cout << endl;

//将2替换为20
replace(v.begin(), v.end(), 2, 20);
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), myPrint);
}

int main() {
test01();
return 0;
}

# replace_if:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

class GreaterFive {
public:
bool operator()(int val) {
if (val > 5)
return true;
return false;
}
};

void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), myPrint);
cout << endl;

//将大于5的元素替换为10
replace_if(v.begin(), v.end(), GreaterFive(), 10);
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), myPrint);
}

int main() {
test01();
return 0;
}

# swap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//注意:交换时使用同种类型的容器
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void myPrint(int val) {
cout << val << " ";
}

void test01() {
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
v2.push_back(i * 10);
}

cout << "交换前:" << endl;
for_each(v1.begin(), v1.end(), myPrint);
cout << endl;
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;

swap(v1, v2);
cout << "交换后:" << endl;
for_each(v1.begin(), v1.end(), myPrint);
cout << endl;
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;
}

int main() {
test01();
return 0;
}

# 算术生成算法 (numeric)

# accumulate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//累加算法
//原型
accumulate(itBeg, itEnd, value);//value为累加起始值
//使用案例
#include<iostream>
#include<vector>
#include<numeric>
using namespace std;

void test01() {
vector<int> v;
for (int i = 1; i < 101; i++) {
v.push_back(i);
}

int result = accumulate(v.begin(), v.end(), 0);
cout << result;
}

int main() {
test01();
return 0;
}

(6.2)fill:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//向容器中填充指定的元素
//原型
fill(itBeg, itEnd, value);
//使用案例
#include<iostream>
#include<vector>
#include<numeric>
using namespace std;

void test01() {
vector<int> v;
v.resize(10);

fill(v.begin(), v.end(), 100);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << "\t";
}
}

int main() {
test01();
return 0;
}

# 常用集合算法

1
2
3
4
5
//简介
//包含在头文件algorithm下
set_intersection(beg1,end1,beg2,end2,begTar); //求两个容器中的交集
set_union(beg1,end1,beg2,end2,begTar); //求两个容器的并集
set_difference(beg1,end1,beg2,end2,begTar); //求两个容器的差集

# set_intersection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void test01() {
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
for (int i = 5; i < 15; i++) {
v2.push_back(i);
}

vector<int> vT;
vT.resize(min(v1.size(), v2.size()));
//set_intersection会返回交集的itEnd
vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vT.begin());
for (vector<int>::iterator it = vT.begin(); it != itEnd; it++) {
cout << *it << "\t";
}
}

int main() {
test01();
return 0;
}

# set_union:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//注意:两个集合必须是有序序列
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void test01() {
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
for (int i = 5; i < 15; i++) {
v2.push_back(i);
}

vector<int> vT;
vT.resize(v1.size() + v2.size());
//set_union会返回交集的itEnd
vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vT.begin());
for (vector<int>::iterator it = vT.begin(); it != itEnd; it++) {
cout << *it << "\t";
}
}

int main() {
test01();
return 0;
}

# set_difference:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//注意:两个集合必须是有序序列
//使用案例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void test01() {
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 20; i++) {
v1.push_back(i);
}
for (int i = 5; i < 15; i++) {
v2.push_back(i);
}

vector<int> vT;
vT.resize(max(v1.size(), v2.size()));
//set_difference会返回交集的itEnd
vector<int>::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vT.begin());
for (vector<int>::iterator it = vT.begin(); it != itEnd; it++) {
cout << *it << "\t";
}
}

int main() {
test01();
return 0;
}

# 文件读写

C++ 中对文件操作需要包含头文件 <fstream>

文件类型分为两种:

  • ** 文本文件 - ** 文件以文本的 ASCII 码形式存储在计算机中。
  • ** 二进制文件 - ** 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们。

操作文件分为三大类:

1.ofstrean:写操作

2.ifstream:读操作

3.fstream:读写操作

# 文件打开方式

打开方式 解释
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在,先删除,再创建
ios::binary 二进制方式

注:文件打开方式可以配合使用,利用 | 操作符。

例如:用二进制方式写文件:ios::binary | ios::out

# 写文件

步骤如下:

1. 包含头文件

2. 创建流对象 // 如:ofstream ofs;

3. 打开文件 // 如:ofs.open (“filePath”,openStyle);

4. 写数据 // 如:ofs <<“写入数据”;

5. 关闭文件 // 如:ofs.close ();

1
2
3
4
5
6
7
8
9
10
11
12
//例如
#include<iostream>
#include<fstream>
using namespace std;
int main() {
ofstream ofs;
ofs.open("./test.txt", ios::out);
ofs << "测试文本1" << endl;
ofs << "测试文本2" << endl;
ofs.close();
return 0;
}

# 读文件

步骤如下:

1. 包含头文件

2. 创建流对象

3. 打开文件,并判断是否打开成功

4. 读数据

5. 关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//例如
#include<iostream>
#include<fstream>
using namespace std;
int main() {
ifstream ifs;
ifs.open("./test.txt", ios::in);
if (!ifs.is_open()) {
//is_open函数判断文件是否打开成功,若成功则返回true
cout << "文件打开失败" << endl;
return 0;
}

//读数据方式1:
//char buf[1024] = { 0 };
//while (ifs >> buf) {
// cout << buf << endl;
//}
//读数据方式2:
//char buf[1024] = { 0 };
//while (ifs.getline(buf, sizeof(buf))) {
// cout << buf << endl;
//}
//读数据方式3:
string buf;
while (getline(ifs, buf)) {
cout << buf << endl;
}
//读数据方式4(不推荐):
//char c;
//while ((c = ifs.get()) != EOF)//EOF:end of file
//{
// cout << c;
//}

ifs.close();
return 0;
}

# 二进制形式读写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//例如
#include<iostream>
#include<fstream>
using namespace std;
class Person {
public:

char m_Name[64];
int m_Age;
};

int main() {
Person p = { "张三", 18 };
fstream fs;
//二进制方式写文件
fs.open("./Person.txt", ios::out | ios::binary);
fs.write((const char*)&p, sizeof(Person));
fs.close();
//二进制方式读文件
fs.open("./Person.txt", ios::in | ios::binary);
if (!fs.is_open()) {
cout << "文件打开失败!" << endl;
return 0;
}
Person p2;
fs.read((char*)&p2, sizeof(Person));
cout << "姓名" << p2.m_Name << "\t" << "年龄" << p2.m_Age << endl;
fs.close();
return 0;
}

# 案例

# 职工管理系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//main.cpp
/*
公司中职工分为三类:普通员工,经理,老板,显示信息时,需要显示职工编号、职工姓名、职工岗位、以及职责。
普通员工职责:完成经理下发的任务。
经理职责:完成老板下发的任务,并下发任务给员工。
老板职责:管理公司所有事务。
*/

/*
管理系统要求实现如下功能:
退出管理程序:退出当前管理系统
增加职工信息:实现批量添加职工功能,将信息录入到文件中,职工信息为:职工编号、姓名、部门编号。
显示职工信息:显示公司内所有职工的信息
删除离职职工:按照编号删除指定的职工
修改职工信息:按照编号修改职工个人信息
查找职工信息:按照职工编号或姓名查找相关人员的信息
排序功能:按照职工编号进行排序
清空所有文档:清空文件中记录的所有职工信息(并拥有防误删功能)
*/
#include"wokerManager.h"
int main() {
wokerManager wm;
while (true) {
wm.start();
char key = wm.cinFunction(1);
switch (key) {
case'0':
wm.end();
break;
case '1':
wm.addEmp();
break;
case '2':
wm.showInfo();
break;
case '3':
wm.deleteEmp();
break;
case '4':
wm.mod_Emp();
break;
case '5':
wm.find_Emp();
break;
case '6':
wm.sort_Emp();
break;
case '7':
wm.renameId();
break;
case '8':
wm.clear();
break;
}
system("pause");
system("cls");
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//wokerManager.h
#pragma once
#include<iostream>
#include<fstream>
#include"Woker.h"
#include"Employee.h"
#include"Manager.h"
#include"Boss.h"
using namespace std;
#define FILENAME "empFile.txt"

class wokerManager
{
public:
wokerManager();
~wokerManager();

char cinFunction(int mod);//控制键入
void start();//开始函数,打印开始菜单
void end();//结束函数
void addEmp();//添加职工
void showInfo();//显示职工信息
Woker** loadFile();//读取文件
void save();//保存文件
int getEmpNum();
void deleteEmp();
int isExist(int id);
void mod_Emp();
void find_Emp();
void sort_Emp();
void clear();
void Sort(int mod);
void renameId();

int empNum;
Woker** wokerArr;
bool m_FileIsEmpty;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
//wokerManager.CPP
#include "wokerManager.h"
wokerManager::wokerManager() {
ifstream ifs;
ifs.open(FILENAME, ios::in);
if (!ifs.is_open()) {
this->empNum = 0;
this->wokerArr = NULL;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
char ch;
ifs >> ch;
if (ifs.eof()) {
this->empNum = 0;
this->wokerArr = NULL;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
this->empNum = getEmpNum();
this->wokerArr = loadFile();
this->m_FileIsEmpty = false;
ifs.close();
}
wokerManager::~wokerManager() {
if (wokerArr != NULL) {
delete[] this->wokerArr;
this->wokerArr = NULL;
}
}
int wokerManager::getEmpNum() {
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id, DeptId;
string name;
int num = 0;
while (ifs >> id && ifs >> name && ifs >> DeptId) {
num++;
}
ifs.close();
return num;
}
void wokerManager::start() {
cout << "******************************" << endl;
cout << "*****欢迎使用职工管理系统!****" << endl;
cout << "********0.退出管理系统********" << endl;
cout << "********1.增加职工信息********" << endl;
cout << "********2.显示职工信息********" << endl;
cout << "********3.删除离职职工********" << endl;
cout << "********4.修改职工信息********" << endl;
cout << "********5.查找职工信息********" << endl;
cout << "********6.按照编号排序********" << endl;
cout << "********7.重置职工编号********" << endl;
cout << "********8.清空所有文档********" << endl;
}
void wokerManager::end() {
cout << "您确定要退出吗?(Y/N):";
while (1) {
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
cout << "欢迎下次使用" << endl;
exit(0);
}
else if (choice == 'N' || choice == 'n') {
return;
}
else {
cout << "输入错误,请重新输入:";
}
}
}
char wokerManager::cinFunction(int mod) {
cout << "请输入您的选择:";
while (1) {
char key;
cin >> key;
switch (key) {
case'0':
if (mod == 2)
break;
case'1':
case'2':
if (mod == 2)
return key;
case'3':
case'4':
case'5':
case'6':
case'7':
case'8':
if (mod == 1)
return key;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "输入错误,请重新输入:";
}
}
void wokerManager::addEmp() {
cout << "请输入你要添加的职工数:";
while (true) {
int addNum = 0;
cin >> addNum;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
if (addNum > 0) {
int newSize = this->empNum + addNum;
Woker** newSpace = new Woker * [newSize];
if (this->wokerArr != NULL) {
for (int i = 0; i < empNum; i++) {
newSpace[i] = this->wokerArr[i];
}
}
for (int i = 0; i < addNum; i++) {
int id, DeptId;
string name;
cout << "请输入第" << i + 1 << "个职工的编号:";
cin >> id;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "请输入第" << i + 1 << "个职工的姓名:";
cin >> name;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "请选择该职工的岗位\t"
<< "1.普通员工" << "2.经理" << "3.老板\t:";
cin >> DeptId;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');

Woker* woker = NULL;
switch (DeptId) {
case 1:
woker = new Employee(id, name, DeptId);
break;
case 2:
woker = new Manager(id, name, DeptId);
break;
case 3:
woker = new Boss(id, name, DeptId);
break;
}
newSpace[empNum + i] = woker;
}
if (this->wokerArr != NULL) {
delete[] this->wokerArr;
this->wokerArr = NULL;
}
this->wokerArr = newSpace;
this->empNum += addNum;
save();
this->m_FileIsEmpty = false;
cout << "添加完成!" << endl;
return;
}
else {
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "输入错误,请重新输入:";
}
}
}
void wokerManager::showInfo() {
if (wokerArr == NULL) {
cout << "暂无职工信息" << endl;
return;
}
for (int i = 0; i < empNum; i++) {
this->wokerArr[i]->showInfo();
}
}
Woker** wokerManager::loadFile() {
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id, DeptId, index = 0;
string name;
Woker** newSpace = new Woker * [this->empNum];
while (ifs >> id && ifs >> name && ifs >> DeptId) {
Woker* woker = NULL;
switch (DeptId) {
case 1:
woker = new Employee(id, name, DeptId);
break;
case 2:
woker = new Manager(id, name, DeptId);
break;
case 3:
woker = new Boss(id, name, DeptId);
break;
}
newSpace[index] = woker;
index++;
}
ifs.close();
return newSpace;
}

void wokerManager::save(){
ofstream ofs;
ofs.open(FILENAME, ios::trunc);
for (int i = 0; i < empNum; i++) {
ofs << this->wokerArr[i]->m_Id << "\t"
<< this->wokerArr[i]->m_Name << "\t"
<< this->wokerArr[i]->m_DeptId << endl;
}
ofs.close();
}

void wokerManager::deleteEmp() {
cout << "请输入要删除的职工的编号:";
int index;
cin >> index;
index = isExist(index);
if (index == -1) {
cout << "职工编号未找到!" << endl;
return;
}
for (int i = index; i < empNum - 1; i++) {
wokerArr[i] = wokerArr[i + 1];
}
save();
this->empNum--;
cout << "删除成功!" << endl;
}
int wokerManager::isExist(int id) {
int index = -1;
for (int i = 0; i < this->empNum; i++) {
if (this->wokerArr[i]->m_Id == id) {
index = i;
break;
}
}
return index;
}
void wokerManager::mod_Emp() {
cout << "请输入要修改的职工的编号:";
int index;
cin >> index;
index = isExist(index);
if (index == -1) {
cout << "职工编号未找到!" << endl;
return;
}
cout << "修改前信息" << endl;
wokerArr[index]->showInfo();
int id, DeptId;
string name;
cout << "请输入修改后职工的编号:";
cin >> id;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "请输入修改后职工的姓名:";
cin >> name;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "请选择修改后职工的岗位\t"
<< "1.普通员工" << "2.经理" << "3.老板\t:";
cin >> DeptId;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');

Woker* woker = NULL;
switch (DeptId) {
case 1:
woker = new Employee(id, name, DeptId);
break;
case 2:
woker = new Manager(id, name, DeptId);
break;
case 3:
woker = new Boss(id, name, DeptId);
break;
}
wokerArr[index] = woker;
save();
cout << "修改完成!" << endl;
}
void wokerManager::find_Emp() {
cout << "请选择查找方式:1.按照编号、2.按照姓名:";
char choice = cinFunction(2);
if (choice == '1') {
cout << "请输入职工的编号:";
int index;
cin >> index;
index = isExist(index);
if (index == -1) {
cout << "职工编号未找到!" << endl;
return;
}
wokerArr[index]->showInfo();
return;
}
else if (choice == '2') {
cout << "请输入职工的姓名:";
string name;
cin >> name;
for (int i = 0; i < this->empNum; i++) {
if (this->wokerArr[i]->m_Name == name) {
this->wokerArr[i]->showInfo();
return;
}
}
cout << "员工未找到" << endl;
}
}
void wokerManager::sort_Emp() {
if (wokerArr == NULL) {
cout << "暂无职工信息" << endl;
return;
}
cout << "请输入排序方式:1.升序、2.降序:";
char choice = cinFunction(2);
switch (choice) {
case '1':
Sort(1);
break;
case '2':
Sort(2);
break;
}
}
void wokerManager::Sort(int mod) {
if (mod == 1) {//升序
for (int i = this->empNum; i > 0; i--) {
//假设最大值的下标为0
int maxIndex = 0;
for (int j = 1; j < i; j++) {
//从1开始遍历至i,当有大于arr[maxIndex]的值时
//将标记最大值的下标指向它
if (wokerArr[j]->m_Id > wokerArr[maxIndex]->m_Id) maxIndex = j;
}
//将最大值交换到找到的位置
Woker* temp = wokerArr[i - 1];//使用i时减一防止越界
wokerArr[i - 1] = wokerArr[maxIndex];
wokerArr[maxIndex] = temp;
}
}
else if (mod == 2) {//降序
for (int i = this->empNum; i > 0; i--) {
//假设最大值的下标为0
int maxIndex = 0;
for (int j = 1; j < i; j++) {
//从1开始遍历至i,当有大于arr[maxIndex]的值时
//将标记最大值的下标指向它
if (wokerArr[j]->m_Id < wokerArr[maxIndex]->m_Id) maxIndex = j;
}
//将最大值交换到找到的位置
Woker* temp = wokerArr[i - 1];//使用i时减一防止越界
wokerArr[i - 1] = wokerArr[maxIndex];
wokerArr[maxIndex] = temp;
}
}
save();
}
void wokerManager::renameId() {
if (this->wokerArr == NULL) {
cout << "暂无职工信息" << endl;
return;
}
for (int i = 0; i < empNum; i++) {
this->wokerArr[i]->m_Id = i + 1;
}
save();
cout << "重置完成!" << endl;
}
void wokerManager::clear() {
cout << "您确定要清空文件吗?(Y/N):";
while (1) {
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
empNum = 0;
save();
cout << "清除成功!" << endl;
return;
}
else if (choice == 'N' || choice == 'n') {
return;
}
else {
cout << "输入错误,请重新输入:";
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Woker.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Woker
{
public:
//显示员工信息
virtual void showInfo() = 0;
//获取部门名称
virtual string getDeptName() = 0;
int m_Id;//职工编号
string m_Name;//职工姓名
int m_DeptId;//部门编号
};
1
2
3
4
5
6
7
8
9
10
//Employee.h
#pragma once
#include"Woker.h"
class Employee:public Woker
{
public:
Employee(int id,string name,int DeptId);
void showInfo();//展示职工信息
string getDeptName();//获取员工部门名称
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Employee.cpp
#include "Employee.h"
Employee::Employee(int id, string name, int DeptId) {
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = DeptId;
}
void Employee::showInfo() {
cout << "职工编号:" << this->m_Id
<< "\t职工姓名:" << this->m_Name
<< "\t职工部门:" << this->getDeptName()
<< "\t职工职责:完成经理下发的任务" << endl;
}
string Employee::getDeptName() {
return string("员工");
}
1
2
3
4
5
6
7
8
9
10
//Manager.h
#pragma once
#include"Woker.h"
class Manager:public Woker
{
public:
Manager(int id, string name, int DeptId);
void showInfo();
string getDeptName();
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Manager.cpp
#include "Manager.h"
Manager::Manager(int id, string name, int DeptId) {
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = DeptId;
}
void Manager::showInfo() {
cout << "职工编号:" << this->m_Id
<< "\t职工姓名:" << this->m_Name
<< "\t职工部门:" << this->getDeptName()
<< "\t职工职责:完成老板下发的任务,并下发任务给员工" << endl;
}
string Manager::getDeptName() {
return string("老板");
}
1
2
3
4
5
6
7
8
9
10
//Boss.h
#pragma once
#include"Woker.h"
class Boss:public Woker
{
public:
Boss(int id, string name, int DeptId);
void showInfo();
string getDeptName();
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Boss.cpp
#include "Boss.h"
Boss::Boss(int id, string name, int DeptId) {
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = DeptId;
}
void Boss::showInfo() {
cout << "职工编号:" << this->m_Id
<< "\t职工姓名:" << this->m_Name
<< "\t职工部门:" << this->getDeptName()
<< "\t职工职责:管理公司所有事务" << endl;
}
string Boss::getDeptName() {
return string("老板");
}

# STL 案例

# STL 案例 1:评委打分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
案例描述:
现有ABCDE 五个选手,需要分别对其进行打分
10名评委对其打分,打分后删除最高分与最低分,取剩余值的平均分作为选手分数
*/
#include<iostream>
#include<string>
#include<vector>
#include<deque>
#include<cstdlib>
#include<ctime>
#include<algorithm>
using namespace std;

class Competitor {
public:
Competitor(string name,int score) {
this->m_name = name;
this->m_score = score;
}
string m_name;
int m_score;
};

void createCompetitor(vector<Competitor>& v) {
string nameSeed = "ABCDE";
for (int i = 0; i < 5; i++) {
string name = "选手";
name += nameSeed[i];
int score = 0;
Competitor c(name, score);
v.push_back(c);
}
}

void setScore(vector<Competitor>& v) {
srand((unsigned)time(NULL));
for (vector<Competitor>::iterator it = v.begin(); it != v.end(); it++) {
deque<int> d;
for (int i = 0; i < 10; i++) {
int score = rand() % 41 + 60;
d.push_back(score);
}
sort(d.begin(), d.end());

d.pop_back();
d.pop_front();
int sum = 0;
for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++) {
sum += *dit;
}
int avg = sum / d.size();
it->m_score = avg;
}
}

void myPrint(vector<Competitor>& v) {
for (vector<Competitor>::iterator it = v.begin(); it != v.end(); it++) {
cout << "name:" << it->m_name << "\tscore:" << it->m_score << endl;
}
}

int main() {
vector<Competitor> v;
createCompetitor(v);
myPrint(v);
setScore(v);
myPrint(v);
system("pause");
return 0;
}

# STL 案例 2:员工分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/*
案例描述:
公司招聘了10名员工(ABCDEFGHIJ),10名员工进入公司后,需要指派其部门
员工信息包括:姓名、工资
部门分为:策划、美术、研发
假设员工均全能,随机为其分配部门与工资
分部门显示员工信息
*/
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<cstdlib>
#include<ctime>
using namespace std;

#define WORKER_NUM 10
#define CEHUA 0
#define MEISHU 1
#define YANFA 2

class Worker {
public:
string m_Name;
int m_Salary;
};

void createWorker(vector<Worker>& v) {
string nameSeed = "ABCDEFGHIJ";
for (int i = 0; i < WORKER_NUM; i++) {
Worker worker;
worker.m_Name = "员工";
worker.m_Name += nameSeed[i];

worker.m_Salary = rand() % 10000 + 10000;

v.push_back(worker);
}
}

void setGroup(vector<Worker>& v, multimap<int, Worker>& m) {
for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++) {
int deptId = rand() % 3;
m.insert(pair<int, Worker>(deptId, *it));
}
}

void showWorkerByGroup(multimap<int, Worker>& m) {
if (m.begin() == m.end())
return;
multimap<int, Worker>::iterator pos = m.find(CEHUA);
int count = m.count(CEHUA), index = 0;
cout << "策划部门:" << endl;
for (; pos != m.end() && index < count; pos++, index++) {
cout << "name:" << pos->second.m_Name << "\tsalary:" << pos->second.m_Salary << endl;
}

pos = m.find(MEISHU);
count = m.count(MEISHU);
index = 0;
cout << "美术部门:" << endl;
for (; pos != m.end() && index < count; pos++, index++) {
cout << "name:" << pos->second.m_Name << "\tsalary:" << pos->second.m_Salary << endl;
}

pos = m.find(YANFA);
count = m.count(YANFA);
index = 0;
cout << "研发部门:" << endl;
for (; pos != m.end() && index < count; pos++, index++) {
cout << "name:" << pos->second.m_Name << "\tsalary:" << pos->second.m_Salary << endl;
}
}

int main() {
srand((unsigned)time(NULL));
vector<Worker> vWorker;
createWorker(vWorker);

multimap<int, Worker> mWorker;
setGroup(vWorker, mWorker);

showWorkerByGroup(mWorker);

system("pause");
return 0;
}

# 演讲比赛流程管理系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
学校举行一场演讲比赛,共有12人参加。比赛共两轮,第一轮为淘汰赛,第二轮为决赛
每名选手都有对应的编号,如10001~10012
比赛方式:分组比赛,每组6人
第一轮分为两个小组,整体按照选手编号进行抽签后顺序演讲
十个评委分别打分,去除最低分和最高分,求平均分为本轮选手成绩
小组演讲结束后,淘汰末三名,前三名晋级
第二轮为决赛,前三名胜出
每轮比赛过后显示晋级选手的信息
*/
//main.cpp
#include<iostream>
#include"Competitor.h"
#include"ManageSystem.h"
using namespace std;

int main() {
srand(time(NULL));
ManageSystem ms;
bool loop = true;
while (loop) {
ms.StartMenu();
int key = -1;
string temp;
cin >> temp;
while (!ms.IsRegexInput(temp)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
key = stoi(temp);
switch (key) {
case 0:
if (ms.Exit())
loop = false;
break;
case 1:
ms.Start();
break;
case 2:
ms.LoadCompetitionResult();
break;
case 3:
ms.Clear();
break;

}
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Competitior.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Competitor
{
public:
Competitor(string name, float score, int number, int m_Group);
void setScore(float score);
float getScore();
void setGroup(int group);
int getGroup();
void showInfo();
int getNumber();

private:
string m_Name;
float m_Score;
int m_Number;
int m_Group;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//Competitor.cpp
#include "Competitor.h"
Competitor::Competitor(string name, float score, int number, int Group) {
this->m_Name = name;
this->m_Score = score;
this->m_Number = number;
this->m_Group = Group;
}

void Competitor::setScore(float score) {
this->m_Score = score;
}

float Competitor::getScore() {
return this->m_Score;
}

void Competitor::setGroup(int group) {
this->m_Group = group;
}

int Competitor::getGroup() {
return this->m_Group;
}

void Competitor::showInfo() {
cout << "选手信息:" << endl;
cout << "编号:" << this->m_Number << "\t姓名:" << this->m_Name << "\t评分:";
printf("%.2f\n", this->m_Score);
}

int Competitor::getNumber() {
return this->m_Number;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//ManageSystem.h
#pragma once
#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
#include<ctime>
#include<cstdlib>
#include<deque>
#include<fstream>
#include<regex>
#include"Competitor.h"
using namespace std;

#define COMPETITOR_NUM 12
#define START_NUM 10001
#define GROUP 0
#define GROUP_1 1
#define GROUP_2 2
#define GROUP_FINAL 3
#define SCORE_TIMES 10
#define PATH "./CompetitionRecord.txt"
#define DATAPATH "./Data.txt"

class ManageSystem
{
public:
ManageSystem();
~ManageSystem();

void StartMenu();
void Start();
void CreateCompetitor();
void SetGroup();
void ShowInfoByGroup();
void SetScore();
void UpCompetitor();
void UpComShow();
void FinalShow();
void SaveData();
void SaveResult();
void LoadCompetitionResult();
void Clear();
bool Exit();
bool IsRegexInput(string str);

private:
vector<Competitor>* ComVector;
int Count;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//ManageSystem.cpp
#include "ManageSystem.h"

bool CompareByScore(Competitor& c1, Competitor& c2) {
return c1.getScore() > c2.getScore();
}

ManageSystem::ManageSystem() {
this->ComVector = new vector<Competitor>;
ifstream ifs;
ifs.open(DATAPATH, ios::in);
if (!ifs.is_open()) {
this->Count = 1;
}
else {
int temp = 1;
string sTemp;
while (ifs >> sTemp);
temp = stoi(sTemp);
this->Count = temp;
}
ifs.close();
}

ManageSystem::~ManageSystem() {
if (this->ComVector != NULL) {
this->ComVector->clear();
delete this->ComVector;
this->ComVector = NULL;
}
}

void ManageSystem::StartMenu() {
cout << "**************************" << endl;
cout << "*****欢迎参加演讲比赛*****" << endl;
cout << "******1.开始演讲比赛******" << endl;
cout << "******2.查看往届记录******" << endl;
cout << "******3.清空比赛记录******" << endl;
cout << "******0.退出比赛程序******" << endl;
cout << "**************************" << endl;
cout << endl;
cout << "请输入您的选择:";
}

void ManageSystem::Start() {

cout << "第 << 1 >> 轮比赛选手正在抽签" << endl;
cout << "---------------------------" << endl;
this->CreateCompetitor();
this->SetGroup();
cout << "抽签后演讲顺序如下:" << endl;
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
cout << it->getNumber() << "\t";
}
cout << endl;
cout << "---------------------------" << endl;
system("pause");

cout << "-------------第1轮正式比赛开始-------------" << endl;
this->SetScore();
this->ShowInfoByGroup();
cout << "-------------第1轮正式比赛结束-------------" << endl;
system("pause");

cout << "-------------第1轮晋级选手信息-------------" << endl;
this->UpCompetitor();
this->UpComShow();
system("pause");
system("cls");

cout << "第 << 2 >> 轮比赛选手正在抽签" << endl;
cout << "---------------------------" << endl;
random_shuffle(this->ComVector->begin(), this->ComVector->end());
cout << "抽签后演讲顺序如下:" << endl;
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
if(it->getGroup() == GROUP_FINAL)
cout << it->getNumber() << "\t";
}
cout << endl;
cout << "---------------------------" << endl;
system("pause");

cout << "-------------第2轮正式比赛开始-------------" << endl;
this->SetScore();
sort(this->ComVector->begin(), this->ComVector->end(), CompareByScore);
cout << "决赛名次:" << endl;
this->UpComShow();
cout << "-------------第2轮正式比赛结束-------------" << endl;
system("pause");
system("cls");
this->FinalShow();
this->SaveResult();
system("pause");
system("cls");
}

void ManageSystem::CreateCompetitor() {
string nameSeed = "ABCDEFGHIJKLMNOPQRTSUVWXYZ";
for (int i = 0; i < COMPETITOR_NUM; i++) {
string name = "选手";
name += nameSeed[i];
Competitor c(name, 0, START_NUM + i, GROUP);
this->ComVector->push_back(c);
}
}

void ManageSystem::SetGroup() {
random_shuffle(this->ComVector->begin(), this->ComVector->end());
int count = 0;
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
if (count < COMPETITOR_NUM / 2) {
it->setGroup(GROUP_1);
count++;
continue;
}
it->setGroup(GROUP_2);
}
}

void ManageSystem::ShowInfoByGroup() {
cout << "第一组选手信息:" << endl;
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
if (it->getGroup() == GROUP_1)
it->showInfo();
}
cout << endl;
cout << "第二组选手信息:" << endl;
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
if (it->getGroup() == GROUP_2)
it->showInfo();
}
}

void ManageSystem::SetScore() {
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
deque<float> d;

float score = 0, sum = 0;
for (int i = 0; i < SCORE_TIMES; i++)
d.push_back((float)(rand() % 40 + 61));

sort(d.begin(), d.end());
d.pop_front();
d.pop_back();
for (int i = 0; i < SCORE_TIMES - 2; i++) {
sum += d.back();
d.pop_back();
}
score = sum / (SCORE_TIMES - 2);

it->setScore(score);
}
}

void ManageSystem::UpCompetitor() {
sort(this->ComVector->begin(), this->ComVector->end(), CompareByScore);
int group1 = 0, group2 = 0;
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
if (group1 < 3 && it->getGroup() == GROUP_1) {
it->setGroup(GROUP_FINAL);
group1++;
continue;
}
if (group2 < 3 && it->getGroup() == GROUP_2) {
it->setGroup(GROUP_FINAL);
group2++;
continue;
}
}
}

void ManageSystem::UpComShow() {
for (vector<Competitor>::iterator it = this->ComVector->begin(); it != this->ComVector->end(); it++) {
if (it->getGroup() == GROUP_FINAL)
it->showInfo();
}
}

void ManageSystem::FinalShow() {
vector<Competitor>::iterator it = this->ComVector->begin();
cout << "冠军:" << endl;
it->showInfo();
it++;
cout << "亚军:" << endl;
it->showInfo();
it++;
cout << "季军:" << endl;
it->showInfo();
}

void ManageSystem::SaveData() {
ofstream ofs;
ofs.open(DATAPATH, ios::out | ios::trunc);
ofs << this->Count << endl;
ofs.close();
}

void ManageSystem::SaveResult() {
ofstream ofs;
ofs.open(PATH, ios::out | ios::app);
if (!ofs.is_open()) {
cout << "比赛结果保存失败!" << endl;
return;
}

vector<Competitor>::iterator it = this->ComVector->begin();
string rank[] = { "冠军:","亚军:","季军:" };
ofs << "第" << this->Count << "届:" << endl;
this->Count++;
this->SaveData();
for (int i = 0; i < 3; i++) {
ofs << rank[i] << "编号:" << it->getNumber() << "、得分:" << it->getScore() << endl;
it++;
}
ofs.close();
cout << "比赛记录已保存,可在查看往届记录中查看" << endl;
}

void ManageSystem::LoadCompetitionResult() {
if (this->Count == 1) {
cout << "暂无比赛记录" << endl;
system("pause");
system("cls");
return;
}
ifstream ifs;
ifs.open(PATH, ios::in);
if (!ifs.is_open()) {
cout << "读取失败!" << endl;
return;
}
string buf;
while (ifs >> buf) {
cout << buf << endl;
}
ifs.close();
system("pause");
system("cls");
}

void ManageSystem::Clear() {
cout << "你确认要清空往届比赛记录吗?(不可恢复!!)[Y/N]:";
char choice = 'N';
cin >> choice;
if (choice == 'y' || choice == 'Y') {
ofstream ofs;
ofs.open(PATH, ios::trunc);
ofs.close();
this->Count = 1;
this->SaveData();
cout << "已全部清除!" << endl;
}
system("pause");
system("cls");
}

bool ManageSystem::Exit() {
cout << "你确认要退出吗?(Y/N):";
char choice = 'a';
cin >> choice;
if (choice == 'y' || choice == 'Y') {
return true;
}
system("cls");
return false;
}

bool ManageSystem::IsRegexInput(string str1){// 使用正则表达式进行匹配
bool flag = true;
regex r("\\d{0,3}");
while (!(flag = regex_match(str1, r)))
{
return flag;
}
return flag;
}

# 机房预约系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
项目介绍:

身份简介:
分别有三种身份使用该程序
学生代表:申请使用机房
教师:审核学生的预约申请
管理员:给学生、教师创建账号

机房简介
机房总共有3间
1.1号机房 最大容量20人
2.2号机房 最多容量50人
3.3号机房 最大容量100人

申请简介:
申请的订单每周由管理员负责清空
学生可以预约未来一周内的机房使用,预约的日期为周一至周五,预约时需要选择预约时段(上午、下午)
教师来审核预约,依据实际情况审核预约通过或者不通过

系统具体需求:
首先进入登录界面,登录身份可选:
学生代表
老师
管理员
退出

每个身份都需要进行验证后,进入子菜单
学生需要输入:学号,姓名,密码
老师需要输入:职工号,姓名,密码
管理员需要输入:管理员ID,密码

学生子菜单:
申请预约 --预约机房
查看自身的预约 --查看自己的预约状态
查看所有预约 --查看全部预约信息以及预约状态
取消预约 --取消自身的预约,预约成功或审核中的预约均可取消
注销登录 --退出登录

教师子菜单:
查看所有预约 --查看全部预约信息以及预约状态
审核预约 --对学生的预约进行审核
注销登录 --退出登录

管理员具体功能
添加账号 --添加学生或教师的账号,需要检测学生编号或教师职工号是否重复
查看账号 --可以选择查看学生或教师的全部信息
查看机房 --查看所有机房的信息
清空预约 --清空所有预约记录
注销登录 --退出登录

*/

这个案例是我开始学代码到现在,代码量最大的,感觉写的很乱,很屎山。自己有都点看不下去 =.=

运行环境为 Windows11,VS2022

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//main.cpp
#include<iostream>
#include"ManageSystem.h"
using namespace std;

int main() {
ifstream ifs;
ifs.open(ADMIN_PATH, ios::in);
if (!ifs.is_open()) {
//如果未打开AdminData.txt文件,则创建AdminData.txt文件,并写入默认管理员账号
ifs.close();
ofstream ofs;
ofs.open(ADMIN_PATH, ios::out);
ofs << 9527 << " " << "Admin" << " " << "admin";
ofs.close();
}
ifs.close();
ifs.open(MACHINE_PATH, ios::in);
if (!ifs.is_open()) {
//如果未打开MachineRoom.txt文件,则创建AdminData.txt文件,并写入默认机房信息
ifs.close();
ofstream ofs;
ofs.open(MACHINE_PATH, ios::out);
ofs << 1 << " " << 20;
ofs << 2 << " " << 50;
ofs << 3 << " " << 100;
ofs.close();
}
ifs.close();

ManageSystem ms;
int choice = -1;
string temp;
ofstream ofs;
ofs.open(MACHINE_PATH, ios::out | ios::app);
ofs.close();
ofs.open(RESERVATION_PATH, ios::out | ios::app);
ofs.close();

while (true) {
ms.StartMenu();
cin >> temp;
while (!ms.CinJudgement(temp, R_TYPE1)) {//使用正则判断输入正确性
cout << "输入错误,请重新输入:";
cin >> temp;
}
choice = stoi(temp);
if (choice > 9) {
choice %= 10;
}
ms.Login(choice);
}
system("pause");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//globalDefine.h
#pragma once

//学生文件
#define STU_PATH "./StudentRepData.txt"
//教师文件
#define TEACHER_PATH "./TeacherData.txt"
//管理员文件
#define ADMIN_PATH "./AdminData.txt"
//机房信息文件
#define MACHINE_PATH "./MachineRoom.txt"
//预约信息文件
#define RESERVATION_PATH "./Reservation.txt"
//正则TYPE1:0,3
#define R_TYPE1 1
//正则TYPE2:0,4
#define R_TYPE2 2
//正则TYPE3:0,9
#define R_TYPE3 3
//正则TYPE4:0,2
#define R_TYPE4 4
//审核状态
//审核中
#define STATUS_ING 1
//审核通过
#define STATUS_PASS 2
//审核未通过
#define STATUS_FAIL 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//ManageSystem.h
#pragma once
#include<iostream>
#include<regex>
#include<vector>
#include"StudentRep.h"
#include"Teacher.h"
#include"Admin.h"
#include"ReservationRecord.h"
#include"MachineRoom.h"
using namespace std;

class ManageSystem
{
private:
vector<StudentRep> v_sr;//用于管理学生代表类
vector<Teacher> v_t;//用于管理教师类
vector<Admin> v_a;//用于管理管理员类
vector<MachineRoom> v_mr;//用于管理机房信息
ReservationRecord* resRec;//用于管理预约信息
public:
ManageSystem();
~ManageSystem();

void LoadFile(int type);//用于从文本读取文件信息到程序
void StartMenu();//开始菜单
bool CinJudgement(string s1, int type);//正则表达式判断输入信息
void Login(int choice);//登录判断
void MachineRoomUpdate();//机房信息更新
void ChildMenu(Base* person, int type);//各类子菜单进入接口
void Exit();//退出程序
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
//ManageSystem.cpp
#include "ManageSystem.h"

void ManageSystem::LoadFile(int type) {
ifstream ifs;
switch (type) {
case 1:
if (!this->v_sr.empty())
this->v_sr.clear();
ifs.open(STU_PATH, ios::in);
if (ifs.is_open()) {
int fId;
string fName, fPasswd;
while (ifs >> fId && ifs >> fName && ifs >> fPasswd) {
this->v_sr.push_back(StudentRep(fId, fName, fPasswd));
}
}
ifs.close();
break;
case 2:
if (!this->v_t.empty())
this->v_t.clear();
ifs.open(TEACHER_PATH, ios::in);
if (ifs.is_open()) {
int fId;
string fName, fPasswd;
while (ifs >> fId && ifs >> fName && ifs >> fPasswd) {
this->v_t.push_back(Teacher(fId, fName, fPasswd));
}
}
ifs.close();
break;
case 3:
if (!this->v_a.empty())
this->v_a.clear();
ifs.open(ADMIN_PATH, ios::in);
if (ifs.is_open()) {
int fId;
string fName, fPasswd;
while (ifs >> fId && ifs >> fName && ifs >> fPasswd) {
this->v_a.push_back(Admin(fId, fName, fPasswd));
}
}
ifs.close();
break;
case 4:
ifs.open(MACHINE_PATH, ios::in);
if (!ifs.is_open()) {
cout << "机房信息读取失败!" << endl;
ifs.close();
return;
}
int room = 0, machineNum = 0;
while (ifs >> room && ifs >> machineNum) {
this->v_mr.push_back(MachineRoom(room, machineNum));
}
ifs.close();
break;
}
}

ManageSystem::ManageSystem() {
this->LoadFile(1);
this->LoadFile(2);
this->LoadFile(3);
this->LoadFile(4);
this->resRec = new ReservationRecord;
}

ManageSystem::~ManageSystem() {
if (!this->v_sr.empty()) {
this->v_sr.clear();
}
if (!this->v_t.empty()) {
this->v_t.clear();
}
if (!this->v_a.empty()) {
this->v_a.clear();
}
this->v_mr.clear();
if (this->resRec != NULL) {
delete this->resRec;
this->resRec = NULL;
}
}

bool ManageSystem::CinJudgement(string s1, int type) {// 使用正则表达式进行匹配
bool flag = true;
regex r("\\d{0,3}");
if (type == R_TYPE1) {
r = regex("\\d{0,3}");
}
else if (type == R_TYPE2) {
r = regex("\\d{0,4}");
}
else if (type == R_TYPE3) {
r = regex("\\d{0,9}");
}
while (!(flag = regex_match(s1, r)))
{
return flag;
}
return flag;
}

void ManageSystem::StartMenu() {
cout << "======================= 欢迎来到传智播客机预约系统 ==============" << endl;
cout << endl << "请输入您的身份" << endl;
cout << "\t\t--------------------------------------------\n";
cout << "\t\t| 1.学生代表 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 2.教 师 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 3.管 理 员 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 0.退 出 |\n";
cout << "\t\t| |\n";
cout << "\t\t--------------------------------------------\n";
cout << "请输入您的选择:";
}

void ManageSystem::Login(int choice) {
if (choice == 0) {
this->Exit();
return;
}

Base* person;
ifstream ifs;
switch (choice) {
case 1:
ifs.open(STU_PATH, ios::in);
if (!ifs.is_open()) {
cout << "文件不存在!" << endl;
ifs.close();
return;
}
break;
case 2:
ifs.open(TEACHER_PATH, ios::in);
if (!ifs.is_open()) {
cout << "文件不存在!" << endl;
ifs.close();
return;
}
break;
case 3:
ifs.open(ADMIN_PATH, ios::in);
if (!ifs.is_open()) {
cout << "文件不存在!" << endl;
ifs.close();
return;
}
break;
}

int id = 0;
string temp;
string passwd;

string usrType[3] = { "学生代表","教师","管理员" };
cout << "请输入您的" << usrType[choice - 1] << "账号:";
cin >> temp;
while (!CinJudgement(temp, R_TYPE3)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
id = stoi(temp);
cout << "请输入您的" << usrType[choice - 1] << "密码:";
cin >> passwd;

int fId;
string fName, fPasswd;
switch (choice) {
case 1:
while (ifs >> fId && ifs >> fName && ifs >> fPasswd) {
if (id == fId && passwd == fPasswd) {
system("cls");
cout << "登录成功!" << endl;
cout << fName << ",欢迎您!" << endl;
person = new StudentRep(fId, fName, fPasswd);
this->ChildMenu(person, 1);
return;
}
}
break;
case 2:
while (ifs >> fId && ifs >> fName && ifs >> fPasswd) {
if (id == fId && passwd == fPasswd) {
system("cls");
cout << "登录成功!" << endl;
cout << fName << ",欢迎您!" << endl;
person = new Teacher(fId, fName, fPasswd);
this->ChildMenu(person, 2);
return;
}
}
break;
case 3:
while (ifs >> fId && ifs >> fName && ifs >> fPasswd) {
if (id == fId && passwd == fPasswd) {
system("cls");
cout << "登录成功!" << endl;
cout << fName << ",欢迎您!" << endl;
person = new Admin(fId, fName, fPasswd);
this->ChildMenu(person, 3);
return;
}
}
break;
}

cout << "验证错误,登录失败!" << endl;
system("pause");
system("cls");
}

void ManageSystem::MachineRoomUpdate() {
ofstream ofs;
ofs.open(MACHINE_PATH, ios::out | ios::trunc);
for (vector<MachineRoom>::iterator it = this->v_mr.begin(); it != this->v_mr.end(); it++) {
ofs << it->roomNum << " " << it->machineNum << endl;
}
ofs.close();
}

void ManageSystem::ChildMenu(Base* person, int type) {
if (type == 1) {
StudentRep* studentRep = (StudentRep*)person;
while (true) {
int choice = studentRep->System();
switch (choice) {
case 0:
if (studentRep->Logout()) {
return;
}
break;
case 1:
studentRep->applyReservation(this->v_mr, *(this->resRec));
break;
case 2:
studentRep->showMyReservation(*(this->resRec));
system("pause");
system("cls");
break;
case 3:
studentRep->showAllReservation(*(this->resRec));
break;
case 4:
studentRep->cancelReservation(this->v_mr, *(this->resRec));
this->MachineRoomUpdate();
break;
}
}
}
else if (type == 2) {
Teacher* teacher = (Teacher*)person;
while (true) {
int choice = teacher->System();
bool pass = false;
switch (choice) {
case 0:
if (teacher->Logout()) {
return;
}
break;
case 1:
teacher->showAllReservation(*(this->resRec));
break;
case 2:
pass = teacher->auditReservation(this->v_mr, *(this->resRec));
if (pass)
this->MachineRoomUpdate();
break;
}
}
}
else if (type == 3) {
Admin* admin = (Admin*)person;
while (true) {
int choice = admin->System();
int type = 0;
switch (choice) {
case 0:
if (admin->Logout()) {
return;
}
break;
case 1:
type = admin->addAccount(this->v_sr, this->v_t, this->v_a);
this->LoadFile(type);
break;
case 2:
admin->showAccount(this->v_sr, this->v_t, this->v_a);
break;
case 3:
admin->showMachine();
break;
case 4:
admin->clearReservation(this->v_mr);
this->MachineRoomUpdate();
this->resRec->LoadFile();
break;
}
}
}
}

void ManageSystem::Exit() {
cout << "您确定要退出吗?[Y/N]:";
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
cout << "欢迎再次使用本系统" << endl;
exit(0);
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
system("pause");
system("cls");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Base.h
#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<regex>
#include<map>
#include"globalDefine.h"
using namespace std;

class Base
{
public:
int m_Id;//账号
string m_Name;//用户名
string m_Passwd;//密码
virtual void openMenu() = 0;//类子菜单
virtual int System() = 0;//类管理
virtual bool Logout() = 0;//登出
bool CinJudgement(string s1, int type);//正则判断
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//Base.cpp
#include"Base.h"

bool Base::CinJudgement(string s1, int type){
bool flag = true;
regex r("\\d{0,3}");
switch (type) {
case 1:
r = regex("\\d{0,3}");
break;
case 2:
r = regex("\\d{0,4}");
break;
case 3:
r = regex("\\d{0,9}");
break;
case 4:
r = regex("\\d{0,2}");
break;
}
while (!(flag = regex_match(s1, r))) {
return flag;
}
return flag;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//StudentRep.h
#pragma once
#include<iostream>
#include"Base.h"
#include"ReservationRecord.h"
#include"MachineRoom.h"
using namespace std;

class StudentRep : public Base
{
public:
StudentRep(int id, string name, string passwd);

int System();
void openMenu();//学生代表子菜单
void applyReservation(vector<MachineRoom>& v_mr, ReservationRecord& resRec);//申请预约
void showMyReservation(ReservationRecord& resRec);//展示我的预约
void showAllReservation(ReservationRecord& resRec);//展示所有预约
void cancelReservation(vector<MachineRoom>& v_mr, ReservationRecord& resRec);//取消我的预约
bool Logout();//登出
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
//StudentRep.cpp
#include "StudentRep.h"

StudentRep::StudentRep(int id, string name, string passwd) {
this->m_Id = id;
this->m_Name = name;
this->m_Passwd = passwd;
}

int StudentRep::System() {
this->openMenu();
string temp;
cin >> temp;
while (!CinJudgement(temp, R_TYPE2)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
int choice = stoi(temp);
if (choice > 9) {
choice %= 10;
}
return choice;
}

void StudentRep::openMenu() {
cout << "学生代表操作系统:" << endl;
cout << "\t\t--------------------------------------------\n";
cout << "\t\t| 1.申请预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 2.查看我的预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 3.查看所有预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 4.取消预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 0.注销登录 |\n";
cout << "\t\t| |\n";
cout << "\t\t--------------------------------------------\n";
cout << "请输入您的选择:";
}

void StudentRep::applyReservation(vector<MachineRoom> &v_mr, ReservationRecord& resRec) {
cout << "机房开放时间为周一至周五:" << endl;
cout << "1、周一\t2、周二\t3、周三\t4、周四\t5、周五" << endl;
cout << "请选择预约的时间:";

int date = 0;
while (true) {
char choice;
cin >> choice;
switch (choice) {
case '1':
date = 1;
break;
case '2':
date = 2;
break;
case '3':
date = 3;
break;
case '4':
date = 4;
break;
case '5':
date = 5;
break;
default:
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "输入错误,请重新输入:";
continue;
}
break;
}

cout << endl;
cout << "请选择申请预约的时间段:" << endl;
cout << "1、上午\t2、下午:";
int time = 0;
while (true) {
char choice;
cin >> choice;
if (choice == '1') {
time = 1;
break;
}
else if (choice == '2') {
time = 2;
break;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "输入错误,请重新输入:";
}

cout << endl;
cout << "1号机房剩余机器:" << v_mr[0].machineNum << endl;
cout << "2号机房剩余机器:" << v_mr[1].machineNum << endl;
cout << "3号机房剩余机器:" << v_mr[2].machineNum << endl;
cout << "请选择机房:";

int room = 0;
while (true) {
char choice;
cin >> choice;
if (choice == '1') {
room = 1;
break;
}
else if (choice == '2') {
room = 2;
break;
}
else if (choice == '3') {
room = 2;
break;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "输入错误,请重新输入:";
}

if (v_mr[room].machineNum < 1) {
cout << "机房已满,预约失败!" << endl;
return;
}

ofstream ofs;
ofs.open(RESERVATION_PATH, ios::app);
ofs << "date:" << date << " ";
ofs << "time:" << time << " ";
ofs << "stuId:" << this->m_Id << " ";
ofs << "stuName:" << this->m_Name << " ";
ofs << "roomId:" << room << " ";
ofs << "status:" << STATUS_ING << endl;
ofs.close();

resRec.LoadFile();

cout << "申请预约成功!" << endl;
system("pause");
system("cls");
}

void StudentRep::showMyReservation(ReservationRecord& resRec) {
for (map<int, map<string, string>>::iterator it = resRec.resRec.begin(); it != resRec.resRec.end(); it++) {
if (stoi(it->second["stuId"]) == this->m_Id) {
cout << "日期:";
switch (stoi(it->second["date"])) {
case 1:
cout << "周一 ";
break;
case 2:
cout << "周二 ";
break;
case 3:
cout << "周三 ";
break;
case 4:
cout << "周四 ";
break;
case 5:
cout << "周五 ";
break;
}

cout << "时间段:";
switch (stoi(it->second["time"])) {
case 1:
cout << "上午 ";
break;
case 2:
cout << "下午 ";
break;
}

cout << "学生账号:" << it->second["stuId"] << " ";
cout << "学生姓名:" << it->second["stuName"] << " ";
cout << "房间号:" << it->second["roomId"] << " ";
cout << "审核状态:";
switch (stoi(it->second["status"])) {
case STATUS_ING:
cout << "审核中" << endl;
break;
case STATUS_PASS:
cout << "审核通过" << endl;
break;
case STATUS_FAIL:
cout << "审核未通过" << endl;
break;
}
}
}
}

void StudentRep::showAllReservation(ReservationRecord& resRec) {
for (map<int, map<string, string>>::iterator it = resRec.resRec.begin(); it != resRec.resRec.end(); it++) {
cout << "日期:";
switch (stoi(it->second["date"])) {
case 1:
cout << "周一 ";
break;
case 2:
cout << "周二 ";
break;
case 3:
cout << "周三 ";
break;
case 4:
cout << "周四 ";
break;
case 5:
cout << "周五 ";
break;
}

cout << "时间段:";
switch (stoi(it->second["time"])) {
case 1:
cout << "上午 ";
break;
case 2:
cout << "下午 ";
break;
}

cout << "学生账号:" << it->second["stuId"] << " ";
cout << "学生姓名:" << it->second["stuName"] << " ";
cout << "房间号:" << it->second["roomId"] << " ";
cout << "审核状态:";
switch (stoi(it->second["status"])) {
case STATUS_ING:
cout << "审核中" << endl;
break;
case STATUS_PASS:
cout << "审核通过" << endl;
break;
case STATUS_FAIL:
cout << "审核未通过" << endl;
break;
}
}
system("pause");
system("cls");
}

void StudentRep::cancelReservation(vector<MachineRoom>& v_mr, ReservationRecord& resRec) {
this->showMyReservation(resRec);
cout << "请选择你要取消的预约(输入记录序号):";
string temp;
cin >> temp;
while (!CinJudgement(temp, R_TYPE3)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
int index = stoi(temp);
int count = 0;
bool findFlag = false;
map<int, map<string, string>>::iterator it;
for (it = resRec.resRec.begin(); it != resRec.resRec.end(); it++) {
if (stoi(it->second["stuId"]) == this->m_Id) {
count++;
if (index == count) {
findFlag = true;
break;
}
}
}
if (findFlag) {
if (stoi(it->second["status"]) == STATUS_PASS) {
switch (stoi(it->second["roomId"])) {
case 1:
v_mr[0].machineNum++;
break;
case 2:
v_mr[1].machineNum++;
break;
case 3:
v_mr[2].machineNum++;
break;
}
}
resRec.resRec.erase(it);
resRec.m_Size--;
resRec.update();
cout << "取消成功!" << endl;
}
else {
cout << "记录未找到" << endl;
}
system("pause");
system("cls");
}

bool StudentRep::Logout() {
cout << "您确定要注销登录吗?[Y/N]:";
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
cout << "注销成功!" << endl;
system("pause");
system("cls");
return true;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "取消登出" << endl;
system("pause");
system("cls");
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Teacher.h
#pragma once
#include<iostream>
#include"Base.h"
#include"MachineRoom.h"
#include"ReservationRecord.h"
using namespace std;

class Teacher : public Base
{
public:
Teacher(int id, string name, string passwd);

int System();
void openMenu();//教师子菜单
void showAllReservation(ReservationRecord& resRec);//展示所有预约信息
bool auditReservation(vector<MachineRoom>& v_mr, ReservationRecord& resRec);//审核预约信息
bool Logout();//登出
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//Teacher.cpp
#include "Teacher.h"

Teacher::Teacher(int id, string name, string passwd) {
this->m_Id = id;
this->m_Name = name;
this->m_Passwd = passwd;
}

int Teacher::System() {
this->openMenu();
string temp;
cin >> temp;
while (!CinJudgement(temp, R_TYPE4)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
int choice = stoi(temp);
if (choice > 9) {
choice %= 10;
}
return choice;
}

void Teacher::openMenu() {
cout << "教师操作系统:" << endl;
cout << "\t\t--------------------------------------------\n";
cout << "\t\t| 1.查看所有预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 2.审核预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 0.注销登录 |\n";
cout << "\t\t| |\n";
cout << "\t\t--------------------------------------------\n";
cout << "请输入您的选择:";
}

void Teacher::showAllReservation(ReservationRecord& resRec) {
for (map<int, map<string, string>>::iterator it = resRec.resRec.begin(); it != resRec.resRec.end(); it++) {
cout << "日期:";
switch (stoi(it->second["date"])) {
case 1:
cout << "周一 ";
break;
case 2:
cout << "周二 ";
break;
case 3:
cout << "周三 ";
break;
case 4:
cout << "周四 ";
break;
case 5:
cout << "周五 ";
break;
}

cout << "时间段:";
switch (stoi(it->second["time"])) {
case 1:
cout << "上午 ";
break;
case 2:
cout << "下午 ";
break;
}

cout << "学生账号:" << it->second["stuId"] << " ";
cout << "学生姓名:" << it->second["stuName"] << " ";
cout << "房间号:" << it->second["roomId"] << " ";
cout << "审核状态:";
switch (stoi(it->second["status"])) {
case STATUS_ING:
cout << "审核中" << endl;
break;
case STATUS_PASS:
cout << "审核通过" << endl;
break;
case STATUS_FAIL:
cout << "审核未通过" << endl;
break;
}
}
system("pause");
system("cls");
}

bool Teacher::auditReservation(vector<MachineRoom>& v_mr, ReservationRecord& resRec) {
for (map<int, map<string, string>>::iterator it = resRec.resRec.begin(); it != resRec.resRec.end(); it++) {
if (stoi(it->second["status"]) == STATUS_ING) {
cout << "日期:";
switch (stoi(it->second["date"])) {
case 1:
cout << "周一 ";
break;
case 2:
cout << "周二 ";
break;
case 3:
cout << "周三 ";
break;
case 4:
cout << "周四 ";
break;
case 5:
cout << "周五 ";
break;
}

cout << "时间段:";
switch (stoi(it->second["time"])) {
case 1:
cout << "上午 ";
break;
case 2:
cout << "下午 ";
break;
}

cout << "学生账号:" << it->second["stuId"] << " ";
cout << "学生姓名:" << it->second["stuName"] << " ";
cout << "房间号:" << it->second["roomId"] << " ";
cout << "审核状态:审核中" << endl;
}
}

cout << "请输入你要审核的预约信息(输入记录序号):";

string temp;
cin >> temp;
while (!CinJudgement(temp, R_TYPE3)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
int index = stoi(temp);
int count = 0;
bool findFlag = false;
map<int, map<string, string>>::iterator it;
for (it = resRec.resRec.begin(); it != resRec.resRec.end(); it++) {
if (stoi(it->second["status"]) == STATUS_ING) {
count++;
if (index == count) {
findFlag = true;
break;
}
}
}
if (findFlag) {
while (true) {
cout << "请输入审核是否通过(Y/N):";
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
it->second["status"] = "2";
cout << "审核完成" << endl;
switch (stoi(it->second["roomId"])) {
case 1:
v_mr[0].machineNum--;
break;
case 2:
v_mr[1].machineNum--;
break;
case 3:
v_mr[2].machineNum--;
break;
}

cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
resRec.update();
system("pause");
system("cls");
return true;
}
else if (choice == 'n' || choice == 'N') {
it->second["status"] = "3";
cout << "审核完成" << endl;

cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
resRec.update();
system("pause");
system("cls");
return false;
}
cout << "输入错误,请重新输入" << endl;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
}
}
else
cout << "记录未找到!" << endl;
system("pause");
system("cls");
return false;
}

bool Teacher::Logout() {
cout << "您确定要注销登录吗?[Y/N]:";
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
cout << "注销成功!" << endl;
system("pause");
system("cls");
return true;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "取消登出" << endl;
system("pause");
system("cls");
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Admin.h
#pragma once
#include<iostream>
#include"Base.h"
#include"Teacher.h"
#include"StudentRep.h"
using namespace std;

class Admin : public Base
{
public:
Admin(int id, string name, string passwd);

int System();
void openMenu();//管理员子菜单
int addAccount(vector<StudentRep>& v_sr, vector<Teacher>& v_t, vector<Admin>& v_a);//账户添加功能
void showAccount(vector<StudentRep>& v_sr, vector<Teacher>& v_t, vector<Admin>& v_a);//展示所有账户功能
void showMachine();//展示机房信息功能
void clearReservation(vector<MachineRoom>& v_mr);//清空预约信息
bool Logout();//登出
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//Admin.cpp
#include "Admin.h"

Admin::Admin(int id, string name, string passwd) {
this->m_Id = id;
this->m_Name = name;
this->m_Passwd = passwd;
}

int Admin::System() {
this->openMenu();
string temp;
cin >> temp;
while (!CinJudgement(temp, R_TYPE2)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
int choice = stoi(temp);
if (choice > 9) {
choice %= 10;
}
return choice;
}

void Admin::openMenu() {
cout << "管理员操作系统:" << endl;
cout << "\t\t--------------------------------------------\n";
cout << "\t\t| 1.添加账号 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 2.查看账号 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 3.查看机房 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 4.清空预约 |\n";
cout << "\t\t| |\n";
cout << "\t\t| 0.注销登录 |\n";
cout << "\t\t| |\n";
cout << "\t\t--------------------------------------------\n";
cout << "请输入您的选择:";
}

void SaveAccountData(string fileName, Base* person) {
ofstream ofs;
ofs.open(fileName, ios::out | ios::app);
if (!ofs.is_open()) {
cout << "Saving fail" << endl;
ofs.close();
return;
}
ofs << person->m_Id << " " << person->m_Name << " " << person->m_Passwd << endl;
ofs.close();
cout << "添加成功!" << endl;
}

int Admin::addAccount(vector<StudentRep>& v_sr, vector<Teacher>& v_t, vector<Admin>& v_a) {
int id = -1;
string name, passwd;
system("cls");
cout << "-------------------------" << endl;
cout << "1.添加学生代表账号" << endl;
cout << "2.添加教师账号" << endl;
cout << "3.添加管理员账号" << endl;
cout << "0.取消添加" << endl;
cout << "请输入您的选择:";
string temp;
cin >> temp;
while (!CinJudgement(temp, R_TYPE1)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
int choice = stoi(temp);

if (choice == 0) {
system("cls");
return 0;
}

string usrType[3] = { "学生代表","教师","管理员" };
cout << "请输入" << usrType[choice -1] << "账号:";
cin >> temp;
while (!CinJudgement(temp, R_TYPE3)) {
cout << "输入错误,请重新输入:";
cin >> temp;
}
id = stoi(temp);

switch (choice) {
case 1:
for (vector<StudentRep>::iterator it = v_sr.begin(); it != v_sr.end(); it++) {
if (id == it->m_Id) {
cout << "账号已存在,创建失败!" << endl;
system("pause");
system("cls");
return 0;
}
}
break;
case 2:
for (vector<Teacher>::iterator it = v_t.begin(); it != v_t.end(); it++) {
if (id == it->m_Id) {
cout << "账号已存在,创建失败!" << endl;
system("pause");
system("cls");
return 0;
}
}
break;
case 3:
for (vector<Admin>::iterator it = v_a.begin(); it != v_a.end(); it++) {
if (id == it->m_Id) {
cout << "账号已存在,创建失败!" << endl;
system("pause");
system("cls");
return 0;
}
}
break;
}

cout << "请输入" << usrType[choice - 1] << "姓名:";
cin >> name;
cout << "请输入" << usrType[choice - 1] << "密码:";
cin >> passwd;

Base* person = NULL;
switch (choice) {
case 1:
person = new StudentRep(id, name, passwd);
SaveAccountData(STU_PATH, person);
break;
case 2:
person = new Teacher(id, name, passwd);
SaveAccountData(TEACHER_PATH, person);
break;
case 3:
person = new Admin(id, name, passwd);
SaveAccountData(ADMIN_PATH, person);
break;
}
delete person;
person = NULL;
system("pause");
system("cls");
return choice;
}

void Admin::showAccount(vector<StudentRep> &v_sr, vector<Teacher>& v_t, vector<Admin>& v_a) {
system("cls");
cout << "学生代表信息:" << endl;
for (vector<StudentRep>::iterator it = v_sr.begin(); it != v_sr.end(); it++) {
cout << "账号:" << it->m_Id << "\t姓名:" << it->m_Name << "\t密码:" << it->m_Passwd << endl;
}

cout << "\n教师信息:" << endl;
for (vector<Teacher>::iterator it = v_t.begin(); it != v_t.end(); it++) {
cout << "账号:" << it->m_Id << "\t姓名:" << it->m_Name << "\t密码:" << it->m_Passwd << endl;
}

cout << "\n管理员信息:" << endl;
for (vector<Admin>::iterator it = v_a.begin(); it != v_a.end(); it++) {
cout << "账号:" << it->m_Id << "\t姓名:" << it->m_Name << endl;
}

system("pause");
system("cls");
}

void Admin::showMachine() {
ifstream ifs;
ifs.open(MACHINE_PATH, ios::in);
if (!ifs.is_open()) {
cout << "File open failed" << endl;
ifs.close();
return;
}
int roomNum = 0, machineNum = 0;
while (ifs >> roomNum && ifs >> machineNum) {
cout << roomNum << "号机房机器数:" << machineNum << endl;
}
ifs.close();
system("pause");
system("cls");
}

void Admin::clearReservation(vector<MachineRoom> &v_mr) {
cout << "您确定要清空预约吗?[Y/N]:";
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
ofstream ofs;
ofs.open(RESERVATION_PATH, ios::trunc);
ofs.close();

v_mr[0].machineNum = 20;
v_mr[1].machineNum = 50;
v_mr[2].machineNum = 100;

cout << "清空成功!" << endl;
system("pause");
system("cls");
return;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "取消清空" << endl;
system("pause");
system("cls");
}

bool Admin::Logout() {
cout << "您确定要注销登录吗?[Y/N]:";
char choice;
cin >> choice;
if (choice == 'Y' || choice == 'y') {
cout << "注销成功!" << endl;
system("pause");
system("cls");
return true;
}
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cout << "取消登出" << endl;
system("pause");
system("cls");
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//MachineRoom.h
#pragma once
#include<iostream>
#include<fstream>
#include<map>
#include"globalDefine.h"
using namespace std;

class MachineRoom
{
public:
int roomNum;
int machineNum;

MachineRoom(int room, int machineNum);
};
1
2
3
4
5
6
7
//MachineRoom.cpp
#include "MachineRoom.h"

MachineRoom::MachineRoom(int room, int machineNum) {
this->roomNum = room;
this->machineNum = machineNum;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//ReservationRecord.h
#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<map>
#include"globalDefine.h"
using namespace std;

class ReservationRecord
{
public:
int m_Size;

ReservationRecord();
~ReservationRecord();

void LoadFile();//预约信息文件读取
void update();//更新预约信息文件

map<int, map<string, string>> resRec;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//ReservationRecord.cpp
#include "ReservationRecord.h"

void divide(map<string, string>& m, string data) {
string key, value;
int pos = data.find(":");
if (pos != -1) {
key = data.substr(0, pos);
value = data.substr(pos + 1, data.size() - pos - 1);
m.insert(pair<string, string>(key, value));
}
}

void ReservationRecord::LoadFile() {
this->m_Size = 0;
string date;
string time;
string stuId;
string stuName;
string roomId;
string status;

ifstream ifs;
ifs.open(RESERVATION_PATH, ios::in);
if (!ifs.is_open()) {
cout << "file open failed" << endl;
ifs.close();
return;
}

if (!this->resRec.empty()) {
this->resRec.clear();
}

while (ifs >> date && ifs >> time && ifs >> stuId && ifs >> stuName && ifs >> roomId && ifs >> status) {
map<string, string> m;
divide(m, date);
divide(m, time);
divide(m, stuId);
divide(m, stuName);
divide(m, roomId);
divide(m, status);
this->m_Size++;
this->resRec.insert(make_pair(this->m_Size, m));
}

ifs.close();
}

ReservationRecord::ReservationRecord() {
this->LoadFile();
}

ReservationRecord::~ReservationRecord() {
if (!this->resRec.empty()) {
this->resRec.clear();
}
}

void ReservationRecord::update() {
ofstream ofs;
if (this->m_Size == 0) {
ofs.open(RESERVATION_PATH, ios::out | ios::trunc);
ofs.close();
return;
}

ofs.open(RESERVATION_PATH, ios::out | ios::trunc);
for (map<int, map<string, string>>::iterator it = this->resRec.begin(); it != this->resRec.end();it++) {
ofs << "date:" << it->second["date"] << " ";
ofs << "time:" << it->second["time"] << " ";
ofs << "stuId:" << it->second["stuId"] << " ";
ofs << "stuName:" << it->second["stuName"] << " ";
ofs << "roomId:" << it->second["roomId"] << " ";
ofs << "status:" << it->second["status"] << endl;
}
ofs.close();

this->LoadFile();
}