C++项目实战——职工管理系统

职工管理系统

在进一步深入学习C++的过程中,我们探索了面向对象编程的思想,并掌握了类和对象的核心概念。我们了解了如何使用封装来保护数据、对象的初始化和清理机制、C++对象模型和this指针的作用。此外,我们还学习了友元、运算符重载、继承、多态以及文件操作。这些知识点将帮助我们构建更复杂的系统,提升编程的结构化和模块化。

为了实践这些新知识,本项目将基于多态性来实现一个常见的期末大作业项目——职工管理系统。这个管理系统可以帮助公司管理所有员工的信息,记录职工的基本情况并简化人事操作流程。

在本教程中,我们将利用C++的面向对象特性来设计和实现一个多态的职工管理系统。系统中,公司职工分为三类:普通员工、经理和老板,每类职工的职责各不相同,具体如下:

  • 普通员工职责:完成经理分配的任务。
  • 经理职责:执行老板布置的任务,同时将任务下发给普通员工。
  • 老板职责:管理公司所有事务,确保公司正常运作。

在此管理系统中,我们需要实现以下核心功能:

  • 增加职工信息:实现批量添加职工功能,将每个职工的编号、姓名和部门编号记录到文件中。
  • 显示职工信息:展示公司所有职工的基本信息。
  • 删除离职职工:根据职工编号删除指定的职工记录。
  • 修改职工信息:根据编号修改职工的个人信息。
  • 查找职工信息:支持根据职工编号或姓名进行查询,快速定位相关人员信息。
  • 按照编号排序:对职工按照编号进行排序,用户可选择不同的排序规则。
  • 清空所有文档:清空文件中记录的职工信息,删除前需确认,以防误删。
  • 退出管理程序:结束当前的管理系统,退出程序。

系统的界面效果图如下所示:

1

用户可以通过不同的选项来操作系统,执行相应的功能。

1、创建管理类

在本项目中,我们将采用分文件的结构来构建整个职工管理系统,这种方式比将所有代码放在单一 .cpp 文件中更有优势。分文件法便于代码维护,每个文件专注于不同的功能模块,使项目结构更清晰、逻辑更分明。

管理类负责的内容如下:

  • 展示菜单界面,与用户进行交互
  • 实现对职工的增、删、改、查操作
  • 处理文件的读写,保存和加载职工信息

首先,我们在项目中创建两个文件夹:一个用于存放头文件,另一个用于存放源文件。在头文件目录中,创建workerManager.h,在源文件目录中创建workerManager.cpp,分别定义和实现管理类。

以下是workerManager.h中管理类的初始定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include<iostream>
using namespace std;


class WorkerManager
{
public:

//构造函数
WorkerManager();

//析构函数
~WorkerManager();

};

此处,WorkerManager类包含构造函数和析构函数的声明,用于初始化和清理管理类的实例。

源文件实现:

workerManager.cpp中,我们为构造函数和析构函数提供空实现,以便为后续功能扩展提供基础。

1
2
3
4
5
6
7
8
9
10
#include "workerManager.h"

WorkerManager::WorkerManager()
{
}

WorkerManager::~WorkerManager()
{
}

至此,职工管理类的基本结构已完成。

2、菜单功能

功能描述:与用户的沟通界面,如图所示:1

2.1 添加成员函数

在管理类workerManager.h中添加成员函数 void Show_Menu();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void WorkerManager::Show_Menu()
{
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 << "********************************************" << endl;
cout << endl;
}

3、创建职工类

在职工管理系统中,职工分为三类:普通员工、经理和老板。我们将这三种类型的职工抽象成一个基类(Worker),并通过多态来管理不同类型的职工。每种职工类型将拥有各自的岗位职责和工作描述,从而实现灵活的扩展性和可维护性。

3.1 创建职工抽象类

职工的分类为:普通员工、经理、老板

将三种职工抽象到一个类(worker)中,利用多态管理不同职工种类

职工的属性为:职工编号、职工姓名、职工所在部门编号

职工的行为为:岗位职责信息描述,获取岗位名称

我们将Worker类作为抽象基类,定义职工的基本属性和行为:

  • 属性:职工编号、职工姓名、职工所在部门编号
  • 行为:显示个人信息、获取岗位名称(纯虚函数)

在头文件文件夹下创建worker.h文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// worker.h
#pragma once
#include <iostream>
#include <string>
using namespace std;

// 职工抽象基类
class Worker
{
public:
// 显示个人信息
virtual void showInfo() = 0;

// 获取岗位名称
virtual string getDeptName() = 0;

int m_Id; // 职工编号
string m_Name; // 职工姓名
int m_DeptId; // 职工所在部门编号
};

该基类的纯虚函数showInfogetDeptName要求子类必须实现具体的行为描述和岗位名称获取功能。

3.2 创建普通员工类

普通员工类继承Worker抽象类,并实现父类中的纯虚函数。普通员工的职责是完成经理分配的任务。

在头文件和源文件文件夹下分别创建employee.hemployee.cpp文件。

employee.h中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// employee.h
#pragma once
#include <iostream>
#include <string>
#include "worker.h"

class Employee : public Worker
{
public:
Employee(int id, string name, int dId);

// 显示个人信息
virtual void showInfo();

// 获取岗位名称
virtual string getDeptName();
};

employee.cpp代码:

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
// employee.cpp
#include "employee.h"
#include <iostream>
#include <string>
using namespace std;

Employee::Employee(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}

// 显示个人信息
void Employee::showInfo()
{
cout << "姓名:" << this->m_Name
<< "\t职工编号:" << this->m_Id
<< "\t岗位:" << this->getDeptName()
<< "\t\t职责:完成经理的任务" << endl;
}

// 获取岗位名称
string Employee::getDeptName()
{
return string("员工");
}

3.3 创建经理类

经理类继承Worker抽象类,并实现父类中的纯虚函数。经理的职责是执行老板布置的任务,并将任务下发给普通员工。

在头文件和源文件文件夹下分别创建manager.hmanager.cpp文件。

manager.h代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// manager.h
#pragma once
#include <iostream>
#include <string>
#include "worker.h"

class Manager : public Worker
{
public:
Manager(int id, string name, int dId);

// 显示个人信息
virtual void showInfo();

// 获取岗位名称
virtual string getDeptName();
};

manager.cpp代码:

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
// manager.cpp
#include "manager.h"
#include <iostream>
#include <string>
using namespace std;

Manager::Manager(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}

// 显示个人信息
void Manager::showInfo()
{
cout << "姓名:" << this->m_Name
<< "\t职工编号:" << this->m_Id
<< "\t岗位:" << this->getDeptName()
<< "\t\t职责:完成老板的任务并下发给员工" << endl;
}

// 获取岗位名称
string Manager::getDeptName()
{
return string("经理");
}

3.4 创建老板类

老板类继承Worker抽象类,并实现父类中的纯虚函数。老板的职责是管理公司所有事务,确保公司运作顺利。

在头文件和源文件文件夹下分别创建boss.hboss.cpp文件。

boss.h代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// boss.h
#pragma once
#include <iostream>
#include <string>
#include "worker.h"

class Boss : public Worker
{
public:
Boss(int id, string name, int dId);

// 显示个人信息
virtual void showInfo();

// 获取岗位名称
virtual string getDeptName();
};

boss.cpp代码:

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
// boss.cpp
#include "boss.h"
#include <iostream>
#include <string>
using namespace std;

Boss::Boss(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}

// 显示个人信息
void Boss::showInfo()
{
cout << "姓名:" << this->m_Name
<< "\t职工编号:" << this->m_Id
<< "\t岗位:" << this->getDeptName()
<< "\t职责:管理公司的所有事务" << endl;
}

// 获取岗位名称
string Boss::getDeptName()
{
return string("首席执行官");
}

至此,我们已经成功创建了包含普通员工、经理和老板的职工类体系,每个子类都继承了Worker抽象类,并实现了岗位职责的描述方法。这种基于多态的设计使得管理系统可以灵活扩展,方便添加新的职工类型或调整现有类型的职责。

3.5 测试多态

职工管理系统.cpp中添加测试函数test(),用于验证职工管理系统中的多态功能是否能够正常运行。通过创建不同类型的职工对象(普通员工、经理和老板)并调用各自的showInfo()方法,我们可以观察到多态的效果。

测试代码如下:

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
#include "worker.h"
#include "employee.h"
#include "manager.h"
#include "boss.h"

void test()
{
Worker* worker = NULL;

// 创建普通员工对象并展示信息
worker = new Employee(1, "张员工", 1);
worker->showInfo();
delete worker;

// 创建经理对象并展示信息
worker = new Manager(2, "李经理", 2);
worker->showInfo();
delete worker;

// 创建老板对象并展示信息
worker = new Boss(3, "王老板", 3);
worker->showInfo();
delete worker;
}

test()函数中,我们通过将Worker*指针指向不同类型的职工对象,并调用各自的showInfo()方法,实现了多态调用。每个对象根据其具体类型展示了不同的岗位职责描述,验证了多态的功能。

运行该测试函数后,终端或控制台应输出每个职工的详细信息,包括姓名、职工编号、岗位和职责描述。运行效果如下图所示:

2多态测试

测试成功后,您可以选择将test()函数的代码注释保留,方便日后测试或调试;或者选择删除该函数,以保持代码的简洁性。

4、添加职工

在本节中,我们实现一个批量添加职工的功能,并将新添加的职工数据保存到文件中。通过该功能,用户可以一次性添加多个职工,并将不同职工类别(普通员工、经理、老板)存储在统一的职工数组中。

4.1 功能分析

需求分析:

用户在批量创建职工时,可以选择不同类别的职工(普通员工、经理、老板)。为了方便管理和存储所有职工信息,我们将使用一个动态数组来存放职工对象的指针,动态扩展数组容量以适应新增数据。

技术实现:

为了实现动态管理不定长度的职工数组,我们将数组定义在堆区,并使用Worker**类型的指针来维护该数组,以便在需要时动态调整数组的大小。

4.2 功能实现

WorkerManager.h 中添加成员属性

WorkerManager类中添加以下两个成员属性,用于记录当前职工人数和职工数组:

1
2
3
4
5
6
// 记录文件中的职工人数
int m_EmpNum;

// 职工数组指针,用于存放所有职工的指针
Worker** m_EmpArray;

初始化成员属性

WorkerManager 的构造函数中,初始化职工人数和职工数组指针:

1
2
3
4
5
6
7
8
9
WorkerManager::WorkerManager()
{
// 初始化职工人数
this->m_EmpNum = 0;

// 初始化职工数组指针为空
this->m_EmpArray = NULL;
}

添加职工函数声明

WorkerManager.h头文件中声明Add_Emp()成员函数,用于添加职工:

1
2
// 增加职工
void Add_Emp();

实现 Add_Emp() 函数

workerManager.cpp 中实现 Add_Emp() 函数,具体步骤如下:

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
void WorkerManager::Add_Emp()
{
cout << "请输入需要增加的职工数量:" << endl;
int addNum = 0;
cin >> addNum;

if (addNum > 0)
{
// 计算新增后的职工总数
int newSize = m_EmpNum + addNum;

// 创建一个新数组,大小为 newSize,用于存放新职工
Worker** newSpace = new Worker*[newSize];

// 将原有职工数组的数据拷贝到新数组中
if (this->m_EmpArray != NULL)
{
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;

// 确保输入的职工编号唯一
while (true)
{
cout << "请输入第" << i + 1 << "名新职工的编号:" << endl;
cin >> id;
int index = this->isExist(id);
if (index == -1) break;
else cout << "该职工编号已存在,请重新输入:" << endl;
}

cout << "请输入第" << i + 1 << "名新职工的姓名:" << endl;
cin >> name;
cout << "请选择第" << i + 1 << "名新职工的职工类型:" << endl;
cout << "1、普通职工" << endl;
cout << "2、经理" << endl;
cout << "3、老板" << endl;
cin >> dSelect;

// 创建对应类型的职工对象并添加到数组中
Worker* worker = NULL;
switch (dSelect)
{
case 1:
worker = new Employee(id, name, dSelect);
break;
case 2:
worker = new Manager(id, name, dSelect);
break;
case 3:
worker = new Boss(id, name, dSelect);
break;
default:
cout << "无效的职工类型" << endl;
break;
}

// 将新职工对象指针存入数组
newSpace[this->m_EmpNum + i] = worker;
}

// 释放旧的职工数组内存
delete[] this->m_EmpArray;

// 更新指针和人数
this->m_EmpArray = newSpace;
this->m_EmpNum = newSize;

cout << "成功添加" << addNum << "名新职工" << endl;

// 更新文件标志并保存新数据到文件
this->m_FileEmpty = false;
this->save();
}
else
{
cout << "输入非法,添加失败" << endl;
}

system("pause");
system("cls");
}

WorkerManager 析构函数中释放内存

为了避免内存泄漏,我们在析构函数中释放m_EmpArray数组的内存:

1
2
3
4
5
6
7
8
WorkerManager::~WorkerManager()
{
if (this->m_EmpArray != NULL)
{
delete[] this->m_EmpArray;
}
}

4.3 测试添加职工功能

在主程序的菜单选项中,调用 Add_Emp() 函数,并根据用户输入展示添加职工的结果。运行程序后,终端将显示添加的职工信息,如下图所示:

03添加员工

至此,职工添加功能已完成,实现了批量添加不同类别的职工并将信息保存至文件。

5、文件交互 - 写文件

在实现职工信息添加后,为了确保数据的持久化,我们需要将数据写入文件中,并在程序启动时从文件中读取这些数据。文件交互的流程可以分为两个步骤:文件写入文件读取

5.1 文件写入

在职工管理系统中,当新职工添加完成后,数据会写入文件以实现持久化。写文件的功能如下:

5.1.1 文件路径设定

workerManager.h文件中定义文件路径常量,并包含fstream头文件:

1
2
#include <fstream>
#define FILENAME "empFile.txt"

5.1.2 成员函数声明

WorkerManager 类中声明 save() 函数,用于将职工数据写入文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
}

5.1.3 保存文件功能测试

在成功添加职工后,调用save()函数,将职工信息写入文件:

1
2
this->save();

测试程序:运行程序添加职工,验证生成的empFile.txt文件中是否记录了新职工的信息。

04写文件

5.2 文件读取

在程序启动时,从文件中加载职工数据到内存中,便于进行后续操作。文件读取功能将包括文件状态判断、数据读取和内存初始化。

5.2.1 文件状态判断

文件状态分为以下三种情况:

  1. 文件未创建:即文件不存在。
  2. 文件存在但无数据:文件存在但内容为空。
  3. 文件存在并包含数据:文件存在且包含职工数据。

workerManager.h 中添加标志属性 m_FileIsEmpty,用于判断文件是否为空:

1
2
//标志文件是否为空
bool m_FileIsEmpty;

在构造函数 WorkerManager::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
32
WorkerManager::WorkerManager()
{
ifstream ifs;
ifs.open(FILENAME, ios::in);

if (!ifs.is_open())
{
// 文件不存在
cout << "文件不存在" << endl;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
this->m_EmpArray = NULL;
ifs.close();
return;
}

// 文件存在,但内容为空
char ch;
ifs >> ch;
if (ifs.eof())
{
cout << "文件为空!" << endl;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
this->m_EmpArray = NULL;
ifs.close();
return;
}

this->m_FileIsEmpty = false;
}

在成功添加职工后,将文件标志更新为非空:

1
2
this->m_FileIsEmpty = false;

5.2.2 获取职工记录数

在文件读取前,需要获取文件中职工的数量以便初始化数组。定义get_EmpNum函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int WorkerManager::get_EmpNum()
{
ifstream ifs(FILENAME, ios::in);
int id, dId, num = 0;
string name;

while (ifs >> id && ifs >> name && ifs >> dId)
{
num++;
}
ifs.close();

return num;
}

在构造函数中调用此函数,获取职工数量并初始化数组:

1
2
3
4
int num = this->get_EmpNum();
this->m_EmpNum = num;
cout << "文件中职工数量为:" << num << endl;

5.2.2 获取职工记录数

在文件读取前,需要获取文件中职工的数量以便初始化数组。定义get_EmpNum函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int WorkerManager::get_EmpNum()
{
ifstream ifs(FILENAME, ios::in);
int id, dId, num = 0;
string name;

while (ifs >> id && ifs >> name && ifs >> dId)
{
num++;
}
ifs.close();

return num;
}

在构造函数中调用此函数,获取职工数量并初始化数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int num = this->get_EmpNum();
this->m_EmpNum = num;
cout << "调试代码:文件中职工数量为:" << num << endl;
this->m_EmpArry = new Worker * [num];
this->init_Emp();

for (int i = 0; i < num; i++)
{
cout << "调试代码:";
cout << "职工号:" << this->m_EmpArry[i]->m_Id
<< " 姓名:" << this->m_EmpArry[i]->m_Name
<< " 部门编号:" << this->m_EmpArry[i]->m_DeptId << endl;
}

手动添加一些职工数据,测试获取职工数量函数

05读文件测试

05读文件测试2

5.2.3 初始化职工数组

WorkerManager类中定义init_Emp()函数,根据文件中的数据初始化m_EmpArray数组:

1
2
//初始化员工
void init_Emp();

在WorkerManager.cpp中实现

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
void WorkerManager::init_Emp()
{
ifstream ifs(FILENAME, ios::in);
int id, dId, index = 0;
string name;

while (ifs >> id && ifs >> name && ifs >> dId)
{
Worker* worker = NULL;
if (dId == 1)
{
worker = new Employee(id, name, dId);
}
else if (dId == 2)
{
worker = new Manager(id, name, dId);
}
else
{
worker = new Boss(id, name, dId);
}

this->m_EmpArray[index++] = worker;
}
ifs.close();
}

在构造函数中调用init_Emp()初始化数据:

//根据职工数创建数组
this->m_EmpArray = new Worker *[this->m_EmpNum];
//初始化职工
init_Emp();

//测试代码
for (int i = 0; i < m_EmpNum; i++)
{
    cout << "职工号: " << this->m_EmpArray[i]->m_Id
        << " 职工姓名: " << this->m_EmpArray[i]->m_Name
        << " 部门编号: " << this->m_EmpArray[i]->m_DeptId << endl;
}

05读文件测试2

至此,实现了完整的文件交互功能,包括文件写入文件读取。测试代码可注释或删除。

6、显示职工

功能描述:显示当前所有职工信息

6.1 显示职工函数声明

workerManager.h 中添加成员函数 void Show_Emp();,用于显示所有职工信息。

1
2
//显示职工
void Show_Emp();

6.2 显示职工函数实现

workerManager.cpp 中实现成员函数 void Show_Emp();,该函数负责根据文件内容判断是否为空,并显示所有职工的详细信息。

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
void WorkerManager::Show_Emp()
{
// 判断文件是否为空
if (this->m_FileEmpty)
{
cout << "文件不存在或为空!" << endl;
}
else
{
// 文件非空,逐个显示职工信息
for (int i = 0; i < this->m_EmpNum; i++)
{
this->m_EmpArray[i]->showInfo(); // 调用职工的 showInfo() 方法显示信息
}
}
system("pause"); // 暂停屏幕显示
system("cls"); // 清空控制台
}

// 根据职工编号查询职工是否存在
int WorkerManager::isExist(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;
}
}
// 调试信息:返回职工的位置
// cout << "调试代码:index=" << index << endl;
return index;
}

// 删除职工
void WorkerManager::Del_Emp()
{
// 判断文件是否为空
if (this->m_FileIsEmpty)
{
cout << "文件不存在或为空,删除失败!" << endl;
}
else
{
cout << "请输入要删除的职工编号:" << endl;
int id;
cin >> id;

// 查找职工是否存在
int index = this->isExist(id);
if (index == -1) // 职工不存在
{
cout << "职工不存在,删除失败!" << endl;
}
else
{
// 删除职工,移动数组元素
for (int i = index; i < this->m_EmpNum - 1; i++)
{
this->m_EmpArray[i] = this->m_EmpArray[i + 1]; // 将下一个职工信息前移
}
this->m_EmpNum--; // 更新职工数量
this->save(); // 保存文件,确保删除后的数据被存储
cout << "删除成功!期待与ta的再次见面~" << endl;
}
}
system("pause"); // 暂停屏幕显示
system("cls"); // 清空控制台
}

6.3 测试显示职工

main 函数中的分支 2 选项中,调用 Show_Emp() 接口进行显示职工功能测试。测试时分别考虑文件为空和文件不为空两种情况。

测试1 - 文件不存在或为空情况

06显示职工测试1

测试效果:当文件为空或不存在时,程序会显示“文件不存在或为空”。

测试2 - 文件存在且有记录情况

06显示职工测试2

测试效果:当文件存在并且有职工记录时,程序将显示职工的详细信息。

至此,显示所有职工信息功能已完整实现。测试代码可以在调试完成后进行注释或删除。

7、删除职工

功能描述:按照职工的编号进行删除职工操作

7.1 删除职工函数声明

workerManager.h 中添加成员函数 void Del_Emp();,用于删除职工。

1
2
//删除职工
void Del_Emp();

7.2 职工是否存在函数声明

为了支持删除职工、修改职工、查找职工等功能,添加一个判断职工是否存在的函数,以便后续调用。

workerManager.h 中添加成员函数 int IsExist(int id);,用于判断职工是否存在。

1
2
3
// 按照职工编号判断职工是否存在, 若存在返回职工在数组中的位置,不存在返回-1
int IsExist(int id);

7.3 职工是否存在函数实现

workerManager.cpp 中实现成员函数 int IsExist(int id);,该函数负责判断职工是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int WorkerManager::isExist(int id)
{
int index = -1; // 初始化为-1,表示未找到职工
for (int i = 0; i < this->m_EmpNum; i++)
{
// 如果职工编号匹配,则返回该职工在数组中的位置
if (this->m_EmpArray[i]->m_Id == id)
{
index = i;
break;
}
}
// 调试信息:返回职工的位置
// cout << "调试代码:index=" << index << endl;
return index; // 若未找到,返回-1
}

7.4 删除职工函数实现

workerManager.cpp 中实现成员函数 void Del_Emp();,该函数实现删除职工操作。

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
void WorkerManager::Del_Emp()
{
// 判断文件是否为空
if (this->m_FileIsEmpty) // 注意:原文中为 m_FileEmpty,修改为 m_FileIsEmpty 更符合命名规范
{
cout << "文件不存在或为空,删除失败!" << endl;
}
else
{
cout << "请输入要删除的职工编号:" << endl;
int id;
cin >> id;

// 查找职工是否存在
int index = this->isExist(id);
if (index == -1) // 职工不存在
{
cout << "职工不存在,删除失败!" << endl;
}
else
{
// 删除职工,移动数组元素
for (int i = index; i < this->m_EmpNum - 1; i++)
{
// 将下一个职工信息前移
this->m_EmpArray[i] = this->m_EmpArray[i + 1];
}
this->m_EmpNum--; // 更新职工数量
this->save(); // 保存文件,确保删除后的数据被存储
cout << "删除成功!期待与ta的再次见面~" << endl;
}
}
system("pause"); // 暂停屏幕显示
system("cls"); // 清空控制台
}

7.5 测试删除职工

main 函数中的分支 3 选项中,调用 Del_Emp() 接口进行删除职工功能的测试。测试时分别考虑删除不存在职工和删除已存在职工两种情况。

测试1 - 删除不存在职工情况

07删除测试1

测试效果:当输入不存在的职工编号时,程序会输出“职工不存在,删除失败”提示。

测试2 - 删除存在的职工情况

删除成功提示图:

07删除测试2

测试效果:当输入存在的职工编号时,程序会成功删除该职工,并提示“删除成功!期待与ta的再次见面~”。

再次显示所有职工信息,确保已经删除

07删除测试3

测试效果:删除职工后,调用显示职工信息接口,确认职工已被成功删除。

至此,删除职工功能已完整实现。测试代码可以在调试完成后进行注释或删除。

8、修改职工

功能描述:能够按照职工的编号对职工信息进行修改并保存。

8.1 修改职工函数声明

workerManager.h 中添加成员函数 void Mod_Emp();,用于修改职工信息。

1
2
//修改职工
void Mod_Emp();

8.2 修改职工函数实现

workerManager.cpp 中实现成员函数 void Mod_Emp();,该函数用于修改指定职工的个人信息。

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
void WorkerManager::Mod_Emp()
{
// 判断文件是否为空
if (this->m_FileEmpty)
{
cout << "文件不存在或者为空,无法修改!" << endl;
return; // 若文件为空,则退出函数
}

// 输入需要修改的职工编号
cout << "请输入需要修改的职工的编号:" << endl;
int id;
cin >> id;

// 查找职工是否存在
int index = this->isExist(id);
if (index == -1) // 职工不存在
{
cout << "查无此人,修改失败!" << endl;
}
else
{
// 显示职工信息
this->m_EmpArray[index]->showInfo();

// 删除原职工对象并准备创建新对象
delete this->m_EmpArray[index];

// 输入新的职工信息
int NewId, NewSelect;
string NewName;

// 确保新的职工ID不重复
while (true)
{
cout << "请输入新的职工号:" << endl;
cin >> NewId;
int index = this->isExist(NewId);
if (index == -1) // 新ID未被占用
break;
else
cout << "此职工ID已存在,请重新输入:" << endl;
}

// 输入新的职工姓名
cout << "请输入新的职工姓名:" << endl;
cin >> NewName;

// 输入新的职工岗位
cout << "请选择新的职工岗位:" << endl;
cout << "1、普通职工" << endl;
cout << "2、经理" << endl;
cout << "3、老板" << endl;
cin >> NewSelect;

// 创建新的职工对象
Worker* worker = NULL;
switch (NewSelect)
{
case 1:
worker = new Employee(NewId, NewName, NewSelect);
break;
case 2:
worker = new Manager(NewId, NewName, NewSelect);
break;
case 3:
worker = new Boss(NewId, NewName, NewSelect);
break;
default:
break;
}

// 更新职工数组中的信息
this->m_EmpArray[index] = worker;
cout << "修改成功!" << endl;

// 保存修改后的数据
this->save();
}

// 暂停并清屏
system("pause");
system("cls");
}

8.3 测试修改职工

main 函数的分支 4 选项中,调用 Mod_Emp() 接口进行修改职工功能的测试。测试时分别考虑修改不存在职工和修改已存在职工两种情况。

测试1 - 修改不存在职工情况

08修改测试1

测试效果:当输入不存在的职工编号时,程序会输出“查无此人,修改失败”提示。

测试2 - 修改存在职工情况,例如将职工 “张三” 改为 “张新三”

08修改测试2

测试效果:修改成功后,系统会显示“修改成功!”并保存修改后的职工信息。

改后再次查看所有职工信息,并确认修改成功

08修改测试3

确认文件中的信息同步更新

测试效果:打开文件,确认修改后的职工信息已正确更新,保存的数据与程序中的显示一致。

至此,修改职工功能已完整实现。测试代码可以在调试完成后进行注释或删除。

9、查找职工

功能描述:提供两种查找职工方式,一种按照职工编号查找,一种按照职工姓名查找。

9.1 查找职工函数声明

workerManager.h 中添加成员函数 void Find_Emp();,用于查找职工信息。

1
2
//查找职工
void Find_Emp();

9.2 查找职工函数实现

workerManager.cpp 中实现成员函数 void Find_Emp();,该函数根据用户选择的方式查找职工。

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::Find_Emp()
{
// 判断文件是否为空
if (this->m_FileEmpty)
{
cout << "文件不存在或者为空,查找失败!" << endl;
return; // 若文件为空,则退出函数
}
else
{
cout << "请选择你的查找方式:" << endl;
cout << "1、按职工编号查找" << endl;
cout << "2、按职工姓名查找" << endl;
int select;
cin >> select;

// 按照职工编号查找
if (select == 1)
{
cout << "请输入需要查找的职工id:" << endl;
int id;
cin >> id;
int index = this->isExist(id);
if (index == -1)
{
cout << "查无此人!" << endl;
}
else
{
this->m_EmpArray[index]->showInfo(); // 显示查找到的职工信息
}
}
// 按照职工姓名查找
else if (select == 2)
{
cout << "请输入需要查找的职工姓名:" << endl;
string name;
cin >> name;

int index = -1;
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i]->m_Name == name)
{
this->m_EmpArray[i]->showInfo(); // 显示所有与该姓名匹配的职工信息
index = 0; // 标记查找结果
}
}
if (index == -1)
{
cout << "查无此人!" << endl;
}
}
}
// 暂停并清屏
system("pause");
system("cls");
}

9.3 测试查找职工

main 函数的分支 5 选项中,调用 Find_Emp() 接口进行查找职工功能的测试。测试时分别考虑按职工编号和按职工姓名查找的不同情况。

测试1 - 按照职工编号查找 - 查找不存在职工

09查找测试01

测试效果:当输入不存在的职工编号时,程序会输出“查无此人!”的提示。

测试2 - 按照职工编号查找 - 查找存在职工

09查找测试02

测试效果:当输入存在的职工编号时,程序会显示该职工的详细信息。

测试3 - 按照职工姓名查找 - 查找不存在职工

09查找测试03

测试效果:当输入不存在的职工姓名时,程序会输出“查无此人!”的提示。

测试4 - 按照职工姓名查找 - 查找存在职工(如果出现重名,也一并显示,在文件中可以添加重名职工)

例如,添加两个名为“张三”的职工,并按照姓名查找“张三”。

09查找测试04-重名

09查找测试04-重名2

测试效果:当存在重名职工时,程序会显示所有匹配的职工信息,确保用户能够看到所有与输入姓名匹配的职工。

至此,查找职工功能已完整实现。测试代码可以在调试完成后进行注释或删除。

10、排序

功能描述:根据职工编号进行排序,排序的顺序由用户指定。

10.1 排序函数声明

workerManager.cpp 中实现成员函数 void Sort_Emp();,该函数将根据用户的选择对职工编号进行排序。

1
2
//排序职工
void Sort_Emp();

10.2 排序函数实现

在workerManager.cpp中实现成员函数 void Sort_Emp();

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
void WorkerManger::Sort_Emp()
{
// 判断文件是否为空
if (this->m_FileEmpty)
{
cout << "文件不存在或者为空,排序失败!" << endl;
system("pause");
system("cls");
}
else
{
int select;
cout << "请选择需要的排序方式:" << endl;
cout << "1、降序排序" << endl;
cout << "2、升序排序" << endl;
cin >> select;

// 根据选择的排序方式进行排序
for (int i = 0; i < this->m_EmpNum; i++)
{
int minOrMax = i;
for (int j = i; j < this->m_EmpNum; j++)
{
if (select == 1) // 降序排序
{
if (this->m_EmpArry[j]->m_Id > this->m_EmpArry[minOrMax]->m_Id)
minOrMax = j;
}
else // 升序排序
{
if (this->m_EmpArry[j]->m_Id < this->m_EmpArry[minOrMax]->m_Id)
minOrMax = j;
}
}
// 交换职工信息
if (minOrMax != i)
{
Worker* temp = this->m_EmpArry[i];
this->m_EmpArry[i] = this->m_EmpArry[minOrMax];
this->m_EmpArry[minOrMax] = temp;
}
}

// 保存排序后的信息并显示
this->save();
this->Show_Emp();
}
}

void WorkerManger::Clear_File()
{
if (this->m_FileEmpty)
{
cout << "文件为不存在或为空,无需操作!" << endl;
system("pause");
system("cls");
}
else
{
cout << "确认清空?此操作无法恢复!文件将会永久删除!" << endl;
cout << "1、确认清空,文件永久删除" << endl;
cout << "任意键取消" << endl;
string select;
cin >> select;
if (select == "1")
{
cout << "确认清空操作请输入“清空”,输入任意内容取消" << endl;
cin >> select;
if (select == "清空")
{
ofstream ofs;
ofs.open(FILENAME, ios::trunc); // 清空文件
ofs.close();

// 清空职工数组
if (this->m_EmpArry != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
delete this->m_EmpArry[i]; // 删除职工对象
}
this->m_EmpNum = 0;
this->m_FileEmpty = true;
delete[] this->m_EmpArry; // 删除职工数组
this->m_EmpArry = NULL;
}
cout << "已清空!" << endl;
}
else
cout << "已取消!" << endl;
}
else
cout << "已取消!" << endl;

system("pause");
system("cls");
}
}

10.3 测试排序功能

main 函数的分支 6 选项中,调用排序职工接口进行排序功能的测试。

添加无序职工

首先我们添加一些职工,职工编号是无序的。例如:

10排序测试1

测试 - 升序排序

10排序测试2

选择升序排序后,职工会根据编号从小到大进行排序。

排序后的文件同步更新:

10排序测试2-1

测试 - 降序排序

10排序测试3

选择降序排序后,职工会根据编号从大到小进行排序。

文件同步更新

10排序测试3-2

至此,职工编号排序功能已实现并经过测试,能够按照用户指定的顺序对职工进行升序或降序排序,并且排序结果会同步保存到文件中。

11、清空文件

功能描述:清空文件中记录的职工数据。

11.1 清空函数声明

workerManager.cpp 中实现 void Clean_File();,该函数将删除文件中的所有数据,并清空职工记录。

1
2
//清空文件
void Clean_File();

11.2 清空函数实现

workerManager.cpp 中实现 void Clean_File();,该函数将删除文件中的所有数据,并清空职工记录。

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
void WorkerManger::Clear_File()
{
if (this->m_FileEmpty)
{
cout << "文件为不存在或为空,无需操作!" << endl;
system("pause");
system("cls");
}
else
{
cout << "确认清空?此操作无法恢复!文件将会永久删除!" << endl;
cout << "1、确认清空,文件永久删除" << endl;
cout << "任意键取消" << endl;
string select;
cin >> select;
if (select == "1")
{
cout << "确认清空操作请输入“清空”,输入任意内容取消" << endl;
cin >> select;
if (select == "清空")
{
// 打开文件进行清空操作
ofstream ofs;
ofs.open(FILENAME, ios::trunc); // 清空文件内容
ofs.close();

// 删除职工数组内容
if (this->m_EmpArry != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
delete this->m_EmpArry[i]; // 删除每个职工对象
}
this->m_EmpNum = 0;
this->m_FileEmpty = true;
delete[] this->m_EmpArry; // 删除职工数组
this->m_EmpArry = NULL;
}
cout << "已清空!" << endl;
}
else
cout << "已取消!" << endl;
}
else
cout << "已取消!" << endl;

system("pause");
system("cls");
}
}

11.3 测试清空文件

main 函数的分支 7 选项中,调用清空文件接口进行测试。

测试 - 确认清空文件

在执行清空文件操作时,系统会询问是否确认清空文件数据,并要求输入确认内容。

11测试清空2

清空操作成功后,文件中的数据将被清除,可以通过打印文件内容来确认。

11测试清空2

再次查看文件中数据,记录已为空

11测试清空1

手动打开文件查看,文件内容已被完全清空:

  • 文件数据已确保清空,该功能需要慎用!

随着清空文件功能的实现,本项目的基于多态的职工管理系统已经完整实现!至此,所有主要功能(增加、删除、修改、查找、排序、清空文件等)已经完成,可以满足基本的职工管理需求。

3、退出功能

在本节中,我们展示了完整的main函数代码,该代码是整个职工管理系统的入口。main函数通过循环和用户输入来控制系统的操作,支持添加、显示、删除、修改、查找、排序职工,以及清空文件等功能。代码中的各个功能通过switch语句进行选择,并对应调用WorkerManger类中的相应成员函数。

3.1 提供功能接口

在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
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include "workerManger.h"
using namespace std;

// 定义操作菜单的选项
enum choice { Exit, Add, Show, Del, Modify, Find, Sort, Clean };

int main()
{
// 创建一个WorkerManger对象 wm,用于管理职工信息
WorkerManger wm;

// 进入主菜单,用户可以根据输入选择操作
while (true)
{
wm.Show_Menu(); // 显示系统菜单
int choice;
cout << "请输入您的选择:" << endl; // 提示用户输入操作选择
cin >> choice; // 获取用户选择

// 根据用户的选择执行不同的功能
switch (choice)
{
case Exit: // 退出系统
wm.EsixSystem(); // 调用退出系统函数
break;
case Add: // 添加职工
wm.Add_Emp(); // 调用添加职工函数
break;
case Show: // 显示职工信息
wm.Show_Emp(); // 调用显示职工信息函数
break;
case Del: // 删除职工
wm.Del_Emp(); // 调用删除职工函数
break;
case Modify: // 修改职工信息
wm.Mod_Emp(); // 调用修改职工信息函数
break;
case Find: // 查找职工
wm.Find_Emp(); // 调用查找职工函数
break;
case Sort: // 排序职工
wm.Sort_Emp(); // 调用排序职工函数
break;
case Clean: // 清空文件数据
wm.Clear_File(); // 调用清空文件函数
break;
default:
system("cls"); // 如果输入无效,清屏
break;
}
}

return 0; // 程序结束
}

代码说明:

  1. 菜单选择与操作:通过enum choice定义了所有可能的菜单选项,并使用switch语句根据用户输入的选择执行相应的功能。每个case语句对应一个功能模块,如添加职工、显示职工、删除职工等。
  2. 循环运行:程序通过while(true)进入一个无限循环,用户输入选择后,系统根据选择执行操作,直到用户选择退出系统。
  3. 操作执行:每个功能模块调用WorkerManger类中的成员函数来实现具体操作。例如,wm.Add_Emp()表示调用Add_Emp函数来添加职工。
  4. 退出系统:当用户选择退出时,调用wm.EsixSystem()来终止程序。

通过这种结构,main函数实现了职工管理系统的菜单化控制,使得程序的操作更加直观和易于扩展。

3.2 实现退出功能

在workerManager.h中提供退出系统的成员函数 void exitSystem();

在workerManager.cpp中提供具体的功能实现

1
2
3
4
5
6
void WorkerManger::EsixSystem()
{
cout << "正在安全退出…… 欢迎下次使用!" << endl;
system("pause");
exit(0);
}

13、 总结

在本次职工管理系统的开发过程中,我们实现了基于C++的面向对象管理系统,通过类和多态的组合,实现了职工信息的管理。各个功能模块的实现使得职工的管理操作非常灵活,并且系统支持文件的持久化存储,能够在程序关闭后仍保留职工数据。

  • 面向对象设计:我们通过继承和多态实现了不同职工类型的统一管理,使得系统结构更清晰,易于扩展。每个职工类型(如员工、经理、老板)都继承自一个基类Worker,并根据岗位需求修改或添加特有的行为。
  • 模块化设计:各个功能如职工增加、删除、修改、查找等,都封装成独立的函数,并且与文件操作结合紧密,确保数据的一致性和持久性。这种模块化设计不仅保证了系统的可维护性,也便于后续功能的扩展。
  • 灵活性与健壮性:在各个功能的实现中,我们考虑了不同的异常情况处理。例如,在删除职工时检查职工是否存在,在修改职工时避免重复编号,在排序职工时确保排序的准确性。这些细节保证了系统的稳定性和可靠性。

通过这次开发,我们实现了一个完整的职工管理系统,涵盖了职工的增、删、改、查、排序和文件管理等基本功能。这个系统不仅展示了如何使用C++中的面向对象编程(OOP)思想,还通过文件操作确保了数据的持久化存储。系统的实现为以后开发更复杂的管理系统奠定了基础。

致谢:

感谢ChatGPT对本文文本部分的指导与修改!这大大节省了本文的创作时间。

感谢ChatGPT对本文部分代码注释的补充!为代码的阅读提供了更佳的便利与体验。

代码原创,需要本项目源码请通过评论区、我的-留言板或邮箱联系免费获取。