对于一个职工管理系统:

公司中职工分为三类:普通员工、经理、老板,显示信息时,需要显示职工编号、职工姓名、职工岗位

管理系统中需要实现的功能如下:

  • 退出管理程序:退出当前管理系统
  • 增加职工信息:实现批量添加职工功能,将信息录入到文件中,职工信息为:职工编号、姓名、部门编号
  • 显示职工信息:显示公司内部所有职工的信息
  • 删除离职职工:按照编号删除指定的职工
  • 修改职工信息:按照编号修改职工个人信息
  • 查找职工信息:按照职工的编号或者职工的姓名进行查找相关的人员信息
  • 按照编号排序:按照职工编号,进行排序,排序规则由用户指定
  • 清空所有文档:清空文件中记录的所有职工信息(清空前需要再次确认,防止误册)

main函数框架

在main函数中仅调用,实现通过头文件导入

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;
#include "WorkerManager.h"

int main() {
WorkerManager wm;
int choice;
while (true) {
wm.Show_Menu();
cout << "Enter your choice: ";
cin >> choice;
switch (choice) {
case 1:
wm.Add_Emp();
break;
case 2:
wm.Show_Emp();
break;
case 3:
wm.Del_Emp();
break;
case 4:
wm.Mod_Emp();
break;
case 5:
wm.Find_Emp();
break;
case 6:
wm.Sort_Emp();
break;
case 7:
wm.Clear_Emp();
break;
case 0:
wm.ExitSystem();
break;
default: {
cout << "Invalid Choice." << endl;
system("cls");
break;
}
}
}
}

系统框架

在管理系统中,我们需要记录的是员工数量,存放员工信息的指针数组,并且判断存储文件是否为空的标识符,同时需要构建各个函数

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
#ifndef WORKERMANAGER_H
#define WORKERMANAGER_H
#pragma once // 确保头文件只被编译一次
#include <iostream>
#include <fstream>
#include "Worker.h"
using namespace std;
#define FILENAME "empFile.txt"

class WorkerManager {
public:
WorkerManager();
void Show_Menu();
void ExitSystem();
void Add_Emp(); // 添加员工
void save(); // 保存结果
int get_Empnum(); // 获取文件中员工数目
void init_Emp(); // 初始化
void Show_Emp(); // 展示内容
void Del_Emp(); // 删除
int IsExist_Emp(int id); // 判断是否存在
void Mod_Emp(); // 修改员工内容
void Find_Emp(); // 查找
void Sort_Emp(); // 排序
void Clear_Emp(); // 清空
~WorkerManager();

int m_EmpNum;
Worker ** m_Emparray; // 重点,指针数组
bool m_FileIsEmpty;
};

由于有多种员工,所以员工分别写成类,并且多态

但统一的属性是一样的,名字,Id,部门(Id)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef WORKER_H
#define WORKER_H
#pragma once // 确保头文件只被编译一次
#include <iostream>
using namespace std;
class Worker {
public:
virtual ~Worker() = default;
virtual void showInfo() = 0;
virtual string getDeptName() = 0;

int m_Id;
string m_Name;
int m_DeptId;
};
#endif //WORKER_H

展示信息和获取部门名称对于不同的员工不一样,函数写为虚函数,Worker成为抽象类

员工分为三种,Staff,Manager,Boss

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef STAFF_H
#define STAFF_H
#pragma once // 确保头文件只被编译一次
#include <iostream>
#include "Worker.h"
using namespace std;
class Staff :public Worker {
public:
Staff(int id, string name, int DeptId);
virtual void showInfo();
virtual string getDeptName();
};
#endif

先导入Worker头文件,创建Staff继承Worker,然后重写虚函数

在具体类中实现信息展示以及构建函数,方便后续new开辟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "Staff.h"
Staff::Staff(int id, string name, int DeptId) {
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = DeptId;

}
void Staff::showInfo() {
cout << "Id: " << m_Id;
cout << "\tName: " << m_Name;
cout << "\tPost: " << this->getDeptName() << endl;
}
string Staff::getDeptName() {
return "Staff";
}

Manager和Boss写法类似

至此可以在main中测试能否分别产生三种类

1
2
3
4
5
6
7
8
9
10
/* test code
Worker *worker = new Staff(1, "John", 1);
worker->showInfo();
delete worker;
worker = new Manager(2, "Ben", 2);
worker->showInfo();
delete worker;
worker = new Boss(3, "Alice", 3);
worker->showInfo();
delete worker;*/

使用virtual后产生一个虚函数指针vfptr能够通过父类指针或引用指向子类对象

Show_Menu()

最简单的一步,只需要写提示内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void WorkerManager::Show_Menu() {
cout << "*************************" << endl;
cout << "Welcome to the WorkerManager System" << endl;
cout << "***** 0.Exit " << endl;
cout << "***** 1.Add Worker Information " << endl;
cout << "***** 2.Show Worker Information " << endl;
cout << "***** 3.Delete Worker Information " << endl;
cout << "***** 4.Modify Worker Information " << endl;
cout << "***** 5.Find Worker Information " << endl;
cout << "***** 6.Sort Worker Information " << endl;
cout << "***** 7.Clear All Information " << endl;
cout << "*************************" << endl;
cout << endl;
}

在main中每次触发结束后都会重新展示以上内容

ExitSystem()

同理只要退出系统提示,但要在函数中加上exit(0),代表退出

1
2
3
4
5
void WorkerManager::ExitSystem() {
cout << "Welcome to the next use!" << endl;
system("pause");
exit(0);
}

save()

为了让结果保存,不会每次重新启动程序就为空,先使用fstream输出

在WorkerManager.h 中定义 #define FILENAME “empFile.txt”

1
2
3
4
5
6
7
8
9
10
void WorkerManager::save() {
ofstream ofs;
ofs.open(FILENAME,ios::out);
for (int i = 0; i < this->m_EmpNum; i++) {
ofs << this->m_Emparray[i]->m_Id << " "
<< this->m_Emparray[i]->m_Name << " "
<< this->m_Emparray[i]->m_DeptId << endl;
}
ofs.close();
}

使得指针数组中的内容被读入文件中并保存

get_Empnum()

既然文件能够被保存,在启动前需要读取文件内容,来获取已有的员工数

1
2
3
4
5
6
7
8
9
10
11
12
13
int WorkerManager::get_Empnum() {
ifstream ifs;
ifs.open(FILENAME,ios::in);
int id;
string name;
int DeptId;
int num = 0;
while (ifs >> id && ifs >> name && ifs >> DeptId) {
num ++;
}
ifs.close();
return num;
}

读取后返回已有员工数

init_Emp()

将文件中的内容用于系统初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void WorkerManager::init_Emp() {
ifstream ifs;
ifs.open(FILENAME,ios::in);
int id;
string name;
int DeptId;
int index = 0;
while (ifs >> id && ifs >> name && ifs >> DeptId)
{
Worker * worker = nullptr;
if (DeptId == 1) {
worker = new Staff(id,name,DeptId);
} else if (DeptId == 2) {
worker = new Manager(id,name,DeptId);
} else if (DeptId == 3) {
worker = new Boss(id,name,DeptId);
}
this->m_Emparray[index] = worker;
index++;
}
ifs.close();
}

先创建一个Worker *的空指针,因为要根据不同类型new

最后将new出来的地址存入指针数组中

WorkerManager()

将刚刚的内容集成到构造函数

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
WorkerManager::WorkerManager() {
// 文件不存在
ifstream ifs;
ifs.open(FILENAME,ios::in);
if (!ifs.is_open()) {
cout << "File not found, initialize" << endl;
this->m_EmpNum = 0;
this->m_Emparray = nullptr;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
char ch;
ifs >> ch;
// 文件存在数据为空
if (ifs.eof()) {
cout << "File empty" << endl;
this->m_EmpNum = 0;
this->m_Emparray = nullptr;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
int num = this -> get_Empnum();
cout << "The EmpNum in File: " << num << endl;
this->m_EmpNum = num;
this->m_Emparray = new Worker*[this->m_EmpNum];
// 将文件数据存入数组
this->init_Emp();
this->m_FileIsEmpty = false;
}

先判断文件是否存在,不存在让m_FileIsEmpty为true,并且人数为0,数组指针指向null,return跳出,同理文件存在但数据为空

如果不为空则计算已有人数,然后new Worker*数组 出来用于初始化存储指针地址;

可以在构造函数底部增加测试代码判断是否能够正常读取文件中已有的数据

1
2
3
4
5
6
/* test code
for (int i = 0; i < this->m_EmpNum; i++) {
cout << "Id:" << this->m_Emparray[i]->m_Id
<< " Name:" << this->m_Emparray[i]->m_Name
<< " dId:" << this->m_Emparray[i]->m_DeptId << endl;
}*/

Add_Emp()

刚刚已经完成了文件保存以及系统数据初始化的过程,接下来要实现系统的第一个功能增加职工

需要考虑增加多少人,数量是否合法,合法后输入员工的信息,注意Id是区别员工的唯一标志(因为名字可能同名),所以在输入Id后需要判断这个Id是否已经存在,存在则跳出重新输入

在这个过程中需要开辟一块新的指针数组空间,用于存放新的指针数组,将原来的拷贝过去后再增加内容

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
void WorkerManager::Add_Emp() {
cout << "Enter the number of Employee: " << endl;
int addNum = 0;
cin >> addNum;
if (addNum > 0)
{
const int newEmpNum = this->m_EmpNum + addNum;
auto ** newSpace = new Worker*[newEmpNum]; // 动态分配一个指针数组
// 将原来空间数据拷贝到新空间
if (this->m_Emparray!= nullptr) {
for (int i = 0; i < this->m_EmpNum; i++) {
newSpace[i] = this->m_Emparray[i];
}
}
// 添加新数据
for (int i = 0; i < addNum; i++) {
int id;
string name;
int dSelect;
cout << "Enter " << i+1 << "th new Employee ID: " << endl;
cin >> id;
if (IsExist_Emp(id)!=-1) {
cout << "Employee ID already exists, please try again" << endl;
i -= 1;
continue;
}
cout << "Enter " << i+1 << "th new Employee Name: " << endl;
cin >> name;
cout << "Enter " << i+1 << "th new Employee Post: " << endl;
cout << "1.Staff" << endl;
cout << "2.Manager" << endl;
cout << "3.Boss" << endl;
cin >> dSelect;
Worker *worker = nullptr;
switch (dSelect) {
case 1: worker = new Staff(id,name,dSelect);
break;
case 2: worker = new Manager(id,name,dSelect);
break;
case 3: worker = new Boss(id,name,dSelect);
break;
default: break;
}
// 将创建的指针保存到数组中
newSpace[this->m_EmpNum+i] = worker;
}
delete[] this->m_Emparray; // 释放原有指针数组空间
this->m_Emparray = newSpace; // 更新新空间指向
this->m_EmpNum = newEmpNum; // 更新新员工数
this->m_FileIsEmpty = false;
cout << "Successfullu Add "<< addNum << " New Workers " <<endl;
// 保存数据
this->save();
} else {
cout << "Error num" << endl;
}
system("pause");
system("cls");
}

IsExist_Emp(const int id)

根据Id搜索判断这个员工是否存在,以-1作为不存在的值,遍历指针数组中的内容,找到具体的索引值返回

1
2
3
4
5
6
7
8
9
10
int WorkerManager::IsExist_Emp(const int id) {
int index = -1;
for (int i = 0; i < this->m_EmpNum; i++) {
if (this->m_Emparray[i]->m_Id == id) {
index = i;
break;
}
}
return index;
}

Show_Emp()

比较复杂和重要的功能在刚刚都已经开发好了,后续只要调用即可

先判断文件内容是否为空,然后多态调用类的成员函数showInfo(),遍历指针数组

1
2
3
4
5
6
7
8
9
10
11
12
13
void WorkerManager::Show_Emp(){
// 判断文件是否为空
if (this->m_FileIsEmpty) {
cout << "File is empty or does not exist! " << endl;
} else {
for (int i = 0; i < this->m_EmpNum; i++) {
// 利用多态调用
this->m_Emparray[i]->showInfo();
}
}
system("pause");
system("cls");
}

Del_Emp()

先找到这个员工存不存在,存在的话让其后续的指针数组整体往前移一位,整体记录的人员数减1即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void WorkerManager::Del_Emp() {
if (this->m_FileIsEmpty) {
cout << "File is empty or does not exist! " << endl;
} else {
cout << "Please enter Worker Id" << endl;
int id;
cin >> id;
int index = this->IsExist_Emp(id);
if (index != -1) // 职工存在
{
for (int i = index; i < this->m_EmpNum-1; i++) {
this->m_Emparray[i] = this->m_Emparray[i+1];
}
this->m_EmpNum--; // 记录人员个数减1
this->save();
cout << "Successfully Delete " << id << endl;
} else {
cout << "Worker does not exist! " << endl;
}
}
system("pause");
system("cls");
}

Mod_Emp()

修改其实类似添加,需要先找到Id所在的索引,然后重新输入,把new的新地址覆盖原来指针数组在索引位置的内容

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
void WorkerManager::Mod_Emp() {
if (this->m_FileIsEmpty) {
cout << "File is empty or does not exist! " << endl;
} else {
cout << "Please enter Worker Id" << endl;
int id;
cin >> id;
int index = this->IsExist_Emp(id);
if (index != -1) {
delete this->m_Emparray[index];
int newId;
string newName;
int newDeptId;
cout << "Find this worker, please enter new information\nnew Id: ";
cin >> newId;
cout << "new Name: ";
cin >> newName;
cout << "new DeptId: " << endl;
cout << "1.Staff" << endl;
cout << "2.Manager" << endl;
cout << "3.Boss" << endl;
cin >> newDeptId;
Worker *worker = nullptr;
switch (newDeptId) {
case 1:
worker = new Staff(id,newName,newDeptId);
break;
case 2:
worker = new Manager(id,newName,newDeptId);
break;
case 3:
worker = new Boss(id,newName,newDeptId);
break;
}
this->m_Emparray[index] = worker;
cout << "Successfully Modified id: " << id << endl;
} else {
cout << "Worker does not exist! " << endl;
}
this->save();
}
system("pause");
system("cls");
}

Find_Emp()

查找方式分为两种,一种是Id索引,和刚刚的IsExist_Emp相同,调用即可,另一种是根据名字,需要遍历内容,最后使用类的成员函数info信息

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
void WorkerManager::Find_Emp() {
if (this->m_FileIsEmpty) {
cout << "File is empty or does not exist! " << endl;
} else {
cout << "Please choose the method:"<< endl;
cout << "1.Find Worker Id" << endl;
cout << "2.Find Worker Name" << endl;
int select;
cin >> select;
if (select == 1) {
int id;
cout << "Please enter Worker Id: ";
cin >> id;
int index = this->IsExist_Emp(id);
if (index != -1) {
cout << "Successfully find this worker, the information as follow:" << endl;
this->m_Emparray[index]->showInfo();
} else {
cout << "Worker does not exist! " << endl;
}
} else if (select == 2) {
bool flag = false;
string name;
cout << "Please enter Worker Name: ";
cin >> name;
for (int i = 0; i < this->m_EmpNum; i++) {
if (this->m_Emparray[i]->m_Name == name) {
this->m_Emparray[i]->showInfo();
flag = true;
}
}
if (flag == false) {
cout << "Worker does not exist! " << endl;
}
} else {
cout << "The error choice! " << endl;
}
}
system("pause");
system("cls");
}

Sort_Emp()

排序Id采用了选择排序法(也可以冒泡,但是选择排序相比冒泡我感觉更直观)

选择排序方式,升序或者降序,从第0个排序到第Num-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
void WorkerManager::Sort_Emp() {
if (this->m_FileIsEmpty) {
cout << "File is empty or does not exist! " << endl;
system("pause");
system("cls");
} else {
cout << "Please choose sort method:" << endl;
cout << "1.Ascending order" << endl;
cout << "2.Descending order" << endl;
int select;
cin >> select;
if (select != 1 && select != 2) {
cout << "Error choice! " << endl;
system("pause");
system("cls");
return;
}
for (int i = 0; i < this->m_EmpNum-1; i++) {
int minormax = i;
for (int j = i+1; j < this->m_EmpNum; j++) {
if (select == 1)
if (this->m_Emparray[minormax]->m_Id > this->m_Emparray[j]->m_Id)
minormax = j;
if (select == 2)
if (this->m_Emparray[minormax]->m_Id < this->m_Emparray[j]->m_Id)
minormax = j;
}
if (i!=minormax) {
Worker *temp = this->m_Emparray[i];
this->m_Emparray[i] = this->m_Emparray[minormax];
this->m_Emparray[minormax] = temp;
}
}
cout << "Successfully sorted! " << endl;
this->save();
this->Show_Emp();
}
}

Clear_Emp()

清除函数,一是要让文件内容清空重新创建,采用ios::trunc可以实现,二是要清除原来开辟的区域,并且让人数置0,空文件标识改为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void WorkerManager::Clear_Emp() {
cout << "Are you sure want to clear?" << endl;
cout << "1.Yes\t2.No" << endl;
int select;
cin >> select;
if (select == 1) {
ofstream ofs(FILENAME,ios::trunc); // 删除后重新创建文件
ofs.close();
if (this->m_Emparray!=nullptr) {
for (int i = 0; i < this->m_EmpNum; i++) {
delete this->m_Emparray[i];
this->m_Emparray[i] = nullptr;
}
delete[] this->m_Emparray;
this->m_Emparray = nullptr;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
}
cout << "Successfully cleared! " << endl;
}
system("pause");
system("cls");
}

~WorkerManager()

析构函数和clear函数相同,但是不删除数据,只释放内存

1
2
3
4
5
6
7
8
9
10
11
WorkerManager::~WorkerManager() {
if (this->m_Emparray != nullptr) {
for (int i = 0; i < this->m_EmpNum; i++) {
if (this->m_Emparray[i] != nullptr) {
delete this->m_Emparray[i];
}
}
delete [] this->m_Emparray;
this->m_Emparray = nullptr;
}
}