目录
一、前言
事先声明:本文章中编写的代码仅用于学习算法思想和编写基础形式使用,并未进行太多的代码优化。因此,若需要对代码进行优化以及异常处理的小伙伴们,可自行添加相关操作,谢谢!
本次主要利用C++来实现一个基于多态的职工信息管理系统职工管理系统,可以用来管理公司内所有员工的信息。
其中,公司中职工分为三类:普通员工、经理、老板。显示信息时,需要显示职工编号、职工姓名、职工岗位、以及职责。其中三种岗位职责内容如下:
1、普通员工职责:完成经理交给的任务
2、经理职责:完成老板交给的任务,并下发任务给员工
3、老板职责:管理公司所有事务
二、功能描述
在职工信息管理系统中,需要实现的功能如下:
1、退出管理程序:退出当前管理系统;
2、增加职工信息:实现批量添加职工功能,将信息录入到文件中,职工信息为:职工编号、姓名、部门编号;
3、显示职工信息:显示公司内部所有职工的信息;
4、删除离职职工:按照编号删除指定的职工;
5、修改职工信息:按照编号修改职工个人信息;
6、查找职工信息:按照职工的编号或者职工的姓名进行查找相关的人员信息;
7、按照编号排序:按照职工编号,进行排序,排序规则由用户指定;
8、清空所有文档:清空文件中记录的所有职工信息 (清空前需要再次确认,防止误删)。
三、大概思路
要实现这个基于多态的职工信息管理系统,首先“基于多态”,我们思考,多态的概念->使用抽象类+继承实现对一类具有共同点的多个实体进行操作,能够使代码结构变得更加清晰,可读性、可维护性高,更淋漓尽致地体现出了C++面向对象的编程思想。
因此,我们可能需要创建一个抽象类和多个实体类,在抽象类中安排好一类事物具有的共同点,在本次程序中根据需求和功能容易得出:因为我们将存放职工的信息,且职工(作为抽象类)包括了老板、经理以及普通员工(作为3种派生类),其中职工均具有职工编号、职工姓名以及职工岗位编号(作为共有的即抽象类中的成员属性),且均有表示岗位的变量,由于岗位名不同,故可以采取岗位获取函数和显示职工信息函数(作为抽象类中的虚函数、派生类中的成员函数)的方式在不同实体中返回对应岗位名。
其次,我们为实现多功能的选择,可能会采用Switch语句实现分支调用,为提高用户可读性和体验,我们需要在适当的位置给予用户提示语、创造用户功能选择界面等等。
整体上,该程序采用标准的分文件处理方式编写,提高结构的清晰度和代码的简洁度,便于快速索引不同部分的代码内容,并利用职工管理类封装需要实现的不同功能以及相关依赖项,将功能函数声明放在其中,并在相应的.cpp文件中实现各个功能的编写,具体细节请看代码实现部分。具体划分如下:
1、职工抽象类头文件
#include"Worker.h"
2、职工派生类头文件
#include"Boss.h"
#include"Manager.h"
#include"Employee.h"
3、职工派生类.cpp成员实现
Boss.cpp
Manager.cpp
Employee.cpp
4、职工管理类(功能封装)头文件
#include"WorkerManager.h"
5、职工管理类.cpp成员实现
WorkManager.cpp
6、主调函数.cpp文件
WorkforceManagementSystem.cpp
四、代码实现
由于本程序代码注释已经比较丰富,保留了大量测试代码的痕迹,完全足够大家进行学习,且基于后期项目学习本需提高代码阅读能力,故在此我不再多进行赘述,详细完全参见代码内注释。谢谢!
值得注意的是,请按照顺序依次查看实现的代码,因为有些注释具有连贯性,重复的类似代码,其注释并未一起重复注释。
一、职工类实现
1、职工抽象类实现 Worker.h
职工抽象类实现(作为职工派生类头文件调用),其中包含了职工共有的编号、姓名、部门编号以及信息展示函数和岗位名称获取函数等。
#pragma once
#include<iostream>
using namespace std;
//职工抽象类
class Worker
{
public:
virtual void showInfo() = 0;
//获取部门岗位
virtual string getDeptName() = 0;
int Id; //编号
string m_Name; //姓名
int m_Dept; //部门编号 1-员工 2-经理 3-老板
};
2、三种职工类的实现
在职工类中首先一定要包含一下抽象类的头文件!!!便于编译器知道三种职工继承的抽象类所在位置。职工类中包含了初始化构造函数、显示信息函数(打印信息即可)以及返回对应岗位的函数等等,三种职工类内实现类似,仅其岗位和名称等有差异,整体上仍存在异曲同工之妙。
1)普通员工类 Employee
i. 员工类头文件 Employee.h
#pragma once
#include"Worker.h"
//职工之员工类
class Employee : public Worker
{
public:
//构造函数
Employee(int id,string name,int dept);
//展示个人信息
void showInfo();
//返回岗位名称
string getDeptName();
};
ii. 员工类成员实现 Employee.cpp
#include"Employee.h"
//构造函数
Employee::Employee(int id, string name, int dept)
{
this->Id = id;
this->m_Name = name;
this->m_Dept = dept;
}
//展示个人信息
void Employee::showInfo()
{
cout << "职工编号:" << this->Id
<< "\t职工姓名:" << this->m_Name
<< "\t岗位:" << this->getDeptName()
<< "\t部门职责:完成经理交给的任务 " << endl;
}
//返回岗位名称
string Employee::getDeptName()
{
//返回的"员工"字符串需要使用string类型转换一下,
//防止编译器以char*类型返回而导致抛出异常
return string("员工");
}
2)经理类 Manager
i. 员工类头文件 Manager.h
#pragma once
#include"Worker.h"
//职工之经理类
class Manager : public Worker
{
public:
//构造函数
Manager(int id, string name, int dept);
//展示个人信息
void showInfo();
//返回岗位名称
string getDeptName();
};
ii. 员工类成员实现 Manager.cpp
#include"Manager.h"
//构造函数
Manager::Manager(int id, string name, int dept)
{
this->Id = id;
this->m_Name = name;
this->m_Dept = dept;
}
//展示个人信息
void Manager::showInfo()
{
cout << "职工编号:" << this->Id
<< "\t职工姓名:" << this->m_Name
<< "\t岗位:" << this->getDeptName()
<< "\t部门职责:完成老板交给的任务,并下发任务给员工 " << endl;
}
//返回岗位名称
string Manager::getDeptName()
{
return string("经理");
}
3)老板类 Boss
i. 员工类头文件 Boss.h
#pragma once
#include"Worker.h"
//职工之老板类
class Boss : public Worker
{
public:
//构造函数
Boss(int id, string name, int dept);
//展示个人信息
void showInfo();
//返回岗位名称
string getDeptName();
};
ii. 员工类成员实现 Boss.cpp
#include"Boss.h"
//构造函数
Boss::Boss(int id, string name, int dept)
{
this->Id = id;
this->m_Name = name;
this->m_Dept = dept;
}
//展示个人信息
void Boss::showInfo()
{
cout << "职工编号:" << this->Id
<< "\t职工姓名:" << this->m_Name
<< "\t岗位:" << this->getDeptName()
<< "\t部门职责:管理公司所有事务 " << endl;
}
//返回岗位名称
string Boss::getDeptName()
{
return string("老板");
}
二、职工管理类实现
1、职工管理类中的功能函数实现
对职工信息管理系统中存在的功能利用函数包装进行实现,便于后期功能的扩展。其中提供选择所有功能的菜单界面函数,且需要实现的功能包括显示添加职工信息、显示职工信息、删除职工信息、修改职工信息、查看职工信息、根据编号排序、清空职工信息、退出系统程序等等。
i. 先展示功能函数名称以及相关的依赖项函数或变量(构造和析构函数除外)
1)菜单展示
void showManu();
2)记录职工人数
int m_EmpNum;
3)创建职工指针
Worker** m_EmpArray;
4)添加职工信息(批量)
void addEmp();
6)保存文件
void saveInfoToFile();
7)判断文件是否为空
bool fileEmpty;
8)记录已有职工人数
int getEmpSum();
9)初始化职工
void initEmployee();
10)显示所有职工
void showEmployee();
11)判断某职工是否存在,存在则返回位置,不存在返回-1
int employeePos(int id);
12)按编号删除职工
void delEmployee();
13)修改职工数据
void modifyEmployeeData();
14)查找职工信息(据编号/姓名)
void findEmployee();
15)职工排序
void employeeSort();
16)清空文件数据
void clearFileData();
17)退出系统
void exitSystem();实现
ii. 各个功能或依赖函数实现
0、添加职工前提依赖实现
根据多态的特性,创建抽象类类型数组存放基类指针以间接存放新加职工,并用m_EmpNum记录已有职工人数,添加时利用newNum记录需要新增的人数,创建新空间存放新增后的总职工,并用Switch语句实现用户选择添加职工的部门岗位,接着释放原本职工数组空间,将新空间赋予职工数组以及新增后的职工人数赋予原职工人数
//记录职工人数
int m_EmpNum;
//创建职工指针
Worker** m_EmpArray;
1、添加职工信息函数实现 void addEmp()
实现过程中需要注意先异常特殊情况的处理,比如输入人数数值是否在正常范围等等。
注:其中可能涉及到文件保存等后面实现的代码调用,不过基本都含有注释,可以先看,看到没有实现部分可跳到那一部分阅读实现的代码(不过调用后面实现的并不多,不用担心看不懂的情况)。
//添加职工
void WorkerManager::addEmp()
{
cout << "请输入您要添加的职工人数:" << endl;
//记录新增人数
int newNum = 0, sign = 3; //sign表示输入的机会仅三次
while (sign)
{
cin >> newNum;
if (newNum > 0)
{
//添加
//记录新增后总人数
int newSize = this->m_EmpNum + newNum;
//开辟新空间存放抽象类指针(间接存放职工信息)
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 < newNum; i++)
{
//新添职工属性
int id = 0; //职工编号
string name; //职工姓名
int dSelect; //职工选择的岗位
cout << "请输入第 " << i + 1 << " 个新添职工编号:" << endl;
cin >> id;
cout << "请输入第 " << i + 1 << " 个新添职工姓名:" << endl;
cin >> name;
cout << "请输入第 " << i + 1 << " 个新添职工的岗位:" << endl;
cout << "1、普通员工" << endl
<< "2、经理" << endl
<< "3、老板" << endl;
cin >> dSelect;
//利用Switch语句创建不同岗位的职工并存放至临时指针worker中
Worker* worker = NULL;
switch (dSelect)
{
case 1:
worker = new Employee(id, name, 1);
break;
case 2:
worker = new Manager(id, name, 2);
break;
case 3:
worker = new Boss(id, name, 3);
break;
default:
cout << "编号输入有误" << endl;
system("pause");
system("cls");
return;
}
//将新职工添加到新空间
newSpace[this->m_EmpNum + i] = worker;
}
//更新已有职工存放信息以及数量
//释放原有空间
delete[] this->m_EmpArray;
//新空间赋值给存放职工信息的空间
this->m_EmpArray = newSpace;
//更新职工数量
this->m_EmpNum = newSize;
//更新文件情况
fileEmpty = false;
cout << "成功添加 " << newNum << " 名新职工!" << endl;
//信息保存至文件
saveInfoToFile();
break; //跳出输入循环
}
else
{
cout << "输入有误!" << endl;
sign--;
if (sign == 0)
{
cout << "抱歉,您输入错误次数过多,已无法输入!" << endl;
}
else
{
cout << "您还剩" << sign << "次输入的机会,请认真重新输入:" << endl;
continue; //跳出本次循环,重新输入
}
}
}
//暂停,给予用户留意时间
system("pause");
system("cls");
}
2、保存信息至文件函数实现
文件的保存是一项相对比较简单的操作,只需要创建文件后将职工空间中的数据写入文件即可。
//保存文件
void WorkerManager::saveInfoToFile()
{
//创建文件
ofstream ofs;
//打开文件
ofs.open(FILENAME, ios::out);
//将信息写入文件
for (int i = 0; i < this->m_EmpNum; ++i)
{
ofs << this->m_EmpArray[i]->Id << " "
<< this->m_EmpArray[i]->m_Name << " "
<< this->m_EmpArray[i]->m_Dept << endl;
}
//关闭文件
ofs.close();
}
3、初始化职工信息函数实现
将之前文件保存的添加的职工信息读取出来,然后存放至后期再次进入系统时重新开辟的存放数据的空间(因为每一次进入系统都要对文件内容进行更新存储,保证信息的实时性和永久存在性)中,用于构造函数中更新保存文件中的职工信息,防止第一次添加完后再次进入时之前的信息记录消失。
//初始化职工
void WorkerManager::initEmployee()
{
//读取数据,添加数据至职工数组
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int dId;
int idenx = 0; //用于标记存放位置
//读取并记录
Worker* worker = NULL;
while (ifs >> id && ifs >> name && ifs >> dId)
{
//根据岗位不同确定不同员工
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[idenx] = worker;
idenx++;
}
//关闭文件
ifs.close();
}
4、信息初始化的构造函数实现
用于对文件以及内部职工信息记录的更新初始化。因为文件中会存在不存在/空的情况以及后期数据更新时获取人数以便开辟合适大小的空间,所以我们在这先定义判断文件是否为空的布尔类型变量和获取添加过的职工人数(通过读取文件便利得出)。
//判断文件是否为空
bool fileEmpty;
//记录已有职工人数
int getEmpSum();
其中文件所处的状态,一般存在三种情况:
1、还未添加职工信息时
1.1 文件还未创建(即文件还不存在)
//1、文件不存在时的初始化
ifstream ifs;
ifs.open(FILENAME, ios::in);
//判断文件是否存在 文件不存在时,ifs.is_open()返回 false
if (!ifs.is_open())
{
//cout << "文件不存在" << endl;
//数据进行初始化
//文件判断为空
fileEmpty = true;
//记录职工人数
int m_EmpNum = 0;
//创建职工指针
Worker** m_EmpArray = NULL;
//关闭文件
ifs.close();
return;
}
2、添加过职工信息时
2.1 但文件内数据被清空了(即文件存在,但是为空)
//2、文件存在,但数据为空
if (ifs.is_open())
{
//首先读取文件中的一个字符,如果文件为空,
//则文件中应只存在一个结尾符,以此判断文件是否为空
char ch;
ifs >> ch;
if (ifs.eof())
{
//文件为空
//cout << "文件存在但无任何数据!" << endl;
//数据进行初始化
//文件判断为空
fileEmpty = true;
//记录职工人数
int m_EmpNum = 0;
//创建职工指针
Worker** m_EmpArray = NULL;
//关闭文件
ifs.close();
return;
}
}
2.2 文件内含有职工数据
重新开辟存放职工数据的空间后,将职工数量以及已有职工个人信息数据全部给存放变量和存放职工的空间。
//3、文件存在,并记录了所有数据时的初始化
int num = this->getEmpSum();
//cout << "现有职工总人数为 " << num << endl;
this->m_EmpNum = num;
//根据现有人数开辟职工数组空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将已有员工存放进去
this->initEmployee();
>>综上,以下是构造函数的完整实现
WorkerManager::WorkerManager()
{
//1、文件不存在时的初始化
ifstream ifs;
ifs.open(FILENAME, ios::in);
//判断文件是否存在 文件不存在时,ifs.is_open()返回 false
if (!ifs.is_open())
{
//cout << "文件不存在" << endl;
//数据进行初始化
//文件判断为空
fileEmpty = true;
//记录职工人数
int m_EmpNum = 0;
//创建职工指针
Worker** m_EmpArray = NULL;
//关闭文件
ifs.close();
return;
}
//2、文件存在,但数据为空
if (ifs.is_open())
{
//首先读取文件中的一个字符,如果文件为空,
//则文件中应只存在一个结尾符,以此判断文件是否为空
char ch;
ifs >> ch;
if (ifs.eof())
{
//文件为空
//cout << "文件存在但无任何数据!" << endl;
//数据进行初始化
//文件判断为空
fileEmpty = true;
//记录职工人数
int m_EmpNum = 0;
//创建职工指针
Worker** m_EmpArray = NULL;
//关闭文件
ifs.close();
return;
}
}
//3、文件存在,并记录了所有数据时的初始化
int num = this->getEmpSum();
//cout << "现有职工总人数为 " << num << endl;
this->m_EmpNum = num;
//根据现有人数开辟职工数组空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将已有员工存放进去
this->initEmployee();
}
5、显示职工信息函数实现
完成信息的更新存储后,就方便随时读取到存放在职工空间中的已有职工的信息数据了
//显示职工
void WorkerManager::showEmployee()
{
//判断文件是否为空或存在
if (this->fileEmpty)
{
cout << "文件不存在或文件为空!" << endl;
}
else
{
for (int i = 0; i < this->getEmpSum(); i++)
{
//利用多态调用程序接口
this->m_EmpArray[i]->showInfo();
}
}
//暂停,给予用户足够留意时间,清屏-保证界面的简洁
system("pause");
system("cls");
}
6、删除、修改和查找的实现
删除、修改、查找这三种功能的实现时存在前提,即我们需要先找到我们要进行删除、修改和删除的职工才能进行后续操作,所以首先我们定义一个根据编号判断某职工是否存在的函数int employeePos(int id),存在则返回编号,否则返回-1.
6.1 根据编号判断职工是否存在的函数实现 int employeePos(int id);
//判断某职工是否存在,存在则返回位置,不存在返回-1
int WorkerManager::employeePos(int id)
{
//未遍历到则直接返回-1
int pos = -1; //位置初始-1
for (int i = 0; i < this->m_EmpNum; ++i)
{
if (id == this->m_EmpArray[i]->Id)
{
//找到职工位置
pos = i;
break;
}
}
return pos;
}
之后。在每一次需要调用文件时,一定要注意判断一下文件的状态,利用判断文件是否为空(bool fileEmpty)的变量即可 ,避免调用异常。
6.2 根据编号删除职工信息函数实现
//按编号删除职工---数据前移覆盖需删数据
void WorkerManager::delEmployee()
{
//判断文件是否存在或记录是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件数据为空,无法删除!" << endl;
}
else
{
int id = 0;
cout << "请输入您想删除的职工对应编号:";
cin >> id;
int index = this->employeePos(id);
if (index != -1)
{
//删除--数据前移,从需删除位置后一个开始前移
for (int i = index; i < this->m_EmpNum-1; i++)
{
//前移--由于是最终会将i+1的位置前移,所以为避免数组溢出,i+1应<this->m_EMpNum
this->m_EmpArray[i] = this->m_EmpArray[i+1];
}
//更新删除后职工的总数
this->m_EmpNum--;
//更新文件中保存的职工--对数据重新写入新文件
this->saveInfoToFile();
//删除完毕
cout << "删除成功!" << endl;
//Bug 当删除最后一个信息时,要将文件空判断变为true,
//避免删除最后一条职工信息后使变为空文件时,使用其他功能不显示用户提示语就按任意键继续
if (this->m_EmpNum == 0)
{
//文件空判断置为真
this->fileEmpty = true;
}
}
else
{
//未找到
cout << "不含该职工编号以及相关信息,删除失败!" << endl;
}
}
//暂停和清屏
system("pause");
system("cls");
}
6.3 按编号指定修改职工信息函数实现
//修改职工数据
void WorkerManager::modifyEmployeeData()
{
//判断文件是否存在或记录是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件数据为空,无法删除!" << endl;
}
else
{
int id = 0;
cout << "请输入您想修改的职工对应编号:";
cin >> id;
int index = this->employeePos(id);
if (index != -1)
{
//创建存放新数据的变量
int newId = 0;
string newName;
int newSelectDeptId = 0;
cout << "编号为 " << id << " 的职工已找到!" << endl<<endl;
//展示需要修改的职工信息,便于用户对比修改
cout << "为方便您修改,以下将展示该职工的相关信息:" << endl;
this->m_EmpArray[index]->showInfo();
//然后释放原职工数据
delete this->m_EmpArray[index];
//临时存放修改数据
cout <<endl<< "请输入新的职工编号:";
cin >> newId;
cout << "请输入新的职工姓名:";
cin >> newName;
cout << "请输入新职工所在岗位编号:" << endl;
cout << "1、普通员工" << endl
<< "2、经理" << endl
<< "3、老板" << endl;
cin >> newSelectDeptId;
//根据选择的不同岗位创建不同岗位的职工空间
//创建临时存放职工的空间
Worker* worker = NULL;
//分支语句实现相应控件创建
switch (newSelectDeptId)
{
case 1:
worker = new Employee(newId, newName, newSelectDeptId);
break;
case 2:
worker = new Manager(newId, newName, newSelectDeptId);
break;
case 3:
worker = new Boss(newId, newName, newSelectDeptId);
break;
default:
//异常退出处理
cout << "新职工部门编号输入有误!" << endl;
system("pause");
system("cls");
return;
}
//更新数组中对应的数据
this->m_EmpArray[index] = worker;
cout << "修改成功!" << endl;
//重新写入修改后的职工数组中的数据并保存到文件中
this->saveInfoToFile();
}
else
{
//不存在
cout << "该职工编号不存在,修改失败!" << endl;
}
}
//给用户留时间并清屏,提高界面的简洁性
system("pause");
system("cls");
}
6.4 查找职工信息函数实现(按编号/姓名)
//查找职工信息(据编号/姓名)
void WorkerManager::findEmployee()
{
//判断文件是否存在或记录是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件数据为空,无法删除!" << endl;
}
else
{
int select = 0;
cout << "请选择您的查找方式:" << endl;
cout << "1、按职工编号查找" << endl
<< "2、按职工姓名查找" << endl;
cin >> select;
if (select == 1) //按编号
{
int id = 0;
cout << "请输入您想查找的职工编号:";
cin >> id;
//判断该职工编号是否存在
int ret = this->employeePos(id);
if (ret != -1)
{
cout << "查找成功!" << endl;
cout << "以下是您查找的编号为 " << id << " 的职工的相关信息:" << endl;
this->m_EmpArray[ret]->showInfo();
}
else
{
cout << "查无此人,查找失败!" << endl;
}
}
if (select == 2) //按姓名
{
string name;
cout << "请输入您查找的职工姓名:" << endl;
cin >> name;
//判断是否存在该姓名的职工,用is_ExitFindName作为标志,存在-true,反之false
//遍历职工的名字取一一比较判断是否存在
bool is_ExitFindName = false;
for (int i = 0; i < this->m_EmpNum; ++i)
{
if (name == this->m_EmpArray[i]->m_Name)
{
//查找到则变为true
is_ExitFindName = true;
//打印找到的职工信息
cout << "查找成功!"<< "姓名为 " << name << " 的职工信息如下:" << endl;
cout << " "; //增加缩进,提高用户观感
//增加打印信息颜色,增强视觉效果
HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(outHandle, 0x06);
//调用显示信息接口
this->m_EmpArray[i]->showInfo();
SetConsoleTextAttribute(outHandle, 0x07);
}
}
if (is_ExitFindName == false)
{
cout << "未找到相应姓名的职工,查找失败!" << endl;
}
}
}
//暂停和清屏
system("pause");
system("cls");
}
7.职工排序函数实现 employeeSort()
对职工排序使用的算法为 选择排序法,此处就不展开讲解了。实际上和冒泡排序比较相似,如若不清楚,可自行搜索相关文档了解。我们对职工排序是根据职工的编号进行排序的,包括了升序排列和降序排列。
//职工排序(选择排序实现)
void WorkerManager::employeeSort()
{
//判断文件是否存在或文件是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件为空,排序失败!" << endl;
system("pause");
system("cls");
return;
}
cout << "请选择您想要的排序方式:" << endl;
cout << "1、按编号升序排列" << endl
<< "2、按编号降序排列" << endl;
int select = 0, sign = 3;
//输入异常处理
while (sign)
{
cin >> select;
if ((select != 1) && (select != 2))
{
cout << "输入选项不存在!" << endl;
sign--;
if (sign == 0)
{
cout << "很抱歉,您输入错误次数过多,已无法进行选择!" << endl;
system("pause");
system("cls");
return;
}
cout << "您还有" << sign << "次机会选择," << "请重新输入:";
continue;
}
break;
}
//选择排序
for (int i = 0; i < this->m_EmpNum; i++)
{
//设置默认最小值或最大值
int minOrMax = i;
for (int j = i + 1; j < this->m_EmpNum; j++)
{
if (select == 1) //升序
{
//如果后面的职工编号 小于 默认最小编号,
//则将后一个职工位置作为默认编号最小的位置
if (this->m_EmpArray[minOrMax]->Id > this->m_EmpArray[j]->Id)
{
minOrMax = j;
}
}
else //降序
{
//与上述同理
if (this->m_EmpArray[minOrMax]->Id < this->m_EmpArray[j]->Id)
{
minOrMax = j;
}
}
}
//判断第i轮比较后原最小编号位置是否变化,若变化则交换相应职工信息
if (i != minOrMax)
{
//交换信息
Worker* temp = this->m_EmpArray[i];
this->m_EmpArray[i] = this->m_EmpArray[minOrMax];
this->m_EmpArray[minOrMax] = temp;
}
}
//排序完成
cout << "排序成功!排序后职工信息如下:" << endl;
//重写并保存排序后的职工信息至文件中
this->saveInfoToFile();
//显示排序后的职工信息
this->showEmployee(); //由于显示信息函数中已经附加暂停和清屏功能,所以此时无需自加了
}
8、清空文件数据函数实现 clearFileData()
先把职工空间类所有的职工信息依次释放,再把整个空间都给释放且置空即可。另外加一些异常处理。
//清空文件数据
void WorkerManager::clearFileData()
{
//判断文件是否存在或是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件为空,清空失败!" << endl;
}
else
{
cout << "您确定要清空吗?" << endl;
cout << "1、确定清空!" << endl << "2、不清空了,返回" << endl;
int select = 0;
cin >> select;
//清空
if (select == 1)
{
//清空文件----删除文件并重新创建一个空文件
ofstream ofs(FILENAME, ios::trunc);
//释放存放职工的所有空间
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
//释放并置空各个数组元素并置空
if (this->m_EmpArray[i] != NULL)
{
delete this->m_EmpArray[i];
this->m_EmpArray[i] = NULL;
}
}
//职工数组空间释放并置空
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
//职工人数置为0
this->m_EmpNum = 0;
//文件清空后,判断文件为空的条件置为true
this->fileEmpty = true;
cout << "清空成功!" << endl;
}
}
}
system("pause");
system("cls");
}
9、退出系统程序函数实现
利用exit(0)直接退出程序。
//退出系统
void WorkerManager::exitSystem()
{
cout << "欢迎下次使用" << endl;
system("pause");
exit(0);
}
10、析构函数实现
释放手动开辟的存放职工的空间。
WorkerManager::~WorkerManager()
{
//堆区内存手动开辟手动释放
if (this->m_EmpArray != NULL)
{
//将每一个元素均释放
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i] != NULL)
{
//释放
delete this->m_EmpArray[i];
}
}
//内部元素释放完毕后再把整个空间均释放,并且置空
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
}
}
iii. 职工管理类实现.cpp文件
#include"WorkManager.h"
WorkerManager::WorkerManager()
{
//1、文件不存在时的初始化
ifstream ifs;
ifs.open(FILENAME, ios::in);
//判断文件是否存在 文件不存在时,ifs.is_open()返回 false
if (!ifs.is_open())
{
//cout << "文件不存在" << endl;
//数据进行初始化
//文件判断为空
fileEmpty = true;
//记录职工人数
int m_EmpNum = 0;
//创建职工指针
Worker** m_EmpArray = NULL;
//关闭文件
ifs.close();
return;
}
//2、文件存在,但数据为空
if (ifs.is_open())
{
//首先读取文件中的一个字符,如果文件为空,
//则文件中应只存在一个结尾符,以此判断文件是否为空
char ch;
ifs >> ch;
if (ifs.eof())
{
//文件为空
//cout << "文件存在但无任何数据!" << endl;
//数据进行初始化
//文件判断为空
fileEmpty = true;
//记录职工人数
int m_EmpNum = 0;
//创建职工指针
Worker** m_EmpArray = NULL;
//关闭文件
ifs.close();
return;
}
}
//3、文件存在,并记录了所有数据时的初始化
int num = this->getEmpSum();
//cout << "现有职工总人数为 " << num << endl;
this->m_EmpNum = num;
//根据现有人数开辟职工数组空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将已有员工存放进去
this->initEmployee();
}
void WorkerManager::showManu()
{
cout << "*******************************************" << endl;
cout << "*********** 欢迎使用职工管理系统!*********" << endl;
cout << "************* 1、增加职工信息 *************" << endl;
cout << "************* 2、显示职工信息 *************" << endl;
cout << "************* 3、删除职工信息 *************" << endl;
cout << "************* 4、修改职工信息 *************" << endl;
cout << "************* 5、查找职工信息 *************" << endl;
cout << "************* 6、按照编号排序 *************" << endl;
cout << "************* 7、清空所有文档 *************" << endl;
cout << "************* 0、退出管理程序 *************" << endl;
cout << "*******************************************" << endl;
cout << endl;
}
//添加职工
void WorkerManager::addEmp()
{
cout << "请输入您要添加的职工人数:" << endl;
//记录新增人数
int newNum = 0, sign = 3; //sign表示输入的机会仅三次
while (sign)
{
cin >> newNum;
if (newNum > 0)
{
//添加
//记录新增后总人数
int newSize = this->m_EmpNum + newNum;
//开辟新空间存放抽象类指针(间接存放职工信息)
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 < newNum; i++)
{
//新添职工属性
int id = 0; //职工编号
string name; //职工姓名
int dSelect; //职工选择的岗位
cout << "请输入第 " << i + 1 << " 个新添职工编号:" << endl;
cin >> id;
cout << "请输入第 " << i + 1 << " 个新添职工姓名:" << endl;
cin >> name;
cout << "请输入第 " << i + 1 << " 个新添职工的岗位:" << endl;
cout << "1、普通员工" << endl
<< "2、经理" << endl
<< "3、老板" << endl;
cin >> dSelect;
//利用Switch语句创建不同岗位的职工并存放至临时指针worker中
Worker* worker = NULL;
switch (dSelect)
{
case 1:
worker = new Employee(id, name, 1);
break;
case 2:
worker = new Manager(id, name, 2);
break;
case 3:
worker = new Boss(id, name, 3);
break;
default:
cout << "编号输入有误" << endl;
system("pause");
system("cls");
return;
}
//将新职工添加到新空间
newSpace[this->m_EmpNum + i] = worker;
}
//更新已有职工存放信息以及数量
//释放原有空间
delete[] this->m_EmpArray;
//新空间赋值给存放职工信息的空间
this->m_EmpArray = newSpace;
//更新职工数量
this->m_EmpNum = newSize;
//更新文件情况
fileEmpty = false;
cout << "成功添加 " << newNum << " 名新职工!" << endl;
//信息保存至文件
saveInfoToFile();
break; //跳出输入循环
}
else
{
cout << "输入有误!" << endl;
sign--;
if (sign == 0)
{
cout << "抱歉,您输入错误次数过多,已无法输入!" << endl;
}
else
{
cout << "您还剩" << sign << "次输入的机会,请认真重新输入:" << endl;
continue; //跳出本次循环,重新输入
}
}
}
//暂停,给予用户留意时间
system("pause");
system("cls");
}
//保存文件
void WorkerManager::saveInfoToFile()
{
//创建文件
ofstream ofs;
//打开文件
ofs.open(FILENAME, ios::out);
//将信息写入文件
for (int i = 0; i < this->m_EmpNum; ++i)
{
ofs << this->m_EmpArray[i]->Id << " "
<< this->m_EmpArray[i]->m_Name << " "
<< this->m_EmpArray[i]->m_Dept << endl;
}
//关闭文件
ofs.close();
}
//记录已有职工人数
int WorkerManager::getEmpSum()
{
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int dId;
int num = 0;
while (ifs >> id && ifs >> name && ifs >> dId)
{
num++;
}
return num;
}
//初始化职工
void WorkerManager::initEmployee()
{
//读取数据,添加数据至职工数组
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int dId;
int idenx = 0; //用于标记存放位置
//读取并记录
Worker* worker = NULL;
while (ifs >> id && ifs >> name && ifs >> dId)
{
//根据岗位不同确定不同员工
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[idenx] = worker;
idenx++;
}
//关闭文件
ifs.close();
}
//显示职工
void WorkerManager::showEmployee()
{
//判断文件是否为空或存在
if (this->fileEmpty)
{
cout << "文件不存在或文件为空!" << endl;
}
else
{
for (int i = 0; i < this->getEmpSum(); i++)
{
//利用多态调用程序接口
this->m_EmpArray[i]->showInfo();
}
}
//暂停,给予用户足够留意时间,清屏-保证界面的简洁
system("pause");
system("cls");
}
//判断某职工是否存在,存在则返回位置,不存在返回-1
int WorkerManager::employeePos(int id)
{
//未遍历到则直接返回-1
int pos = -1; //位置初始-1
for (int i = 0; i < this->m_EmpNum; ++i)
{
if (id == this->m_EmpArray[i]->Id)
{
//找到职工位置
pos = i;
break;
}
}
return pos;
}
//按编号删除职工---数据前移覆盖需删数据
void WorkerManager::delEmployee()
{
//判断文件是否存在或记录是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件数据为空,无法删除!" << endl;
}
else
{
int id = 0;
cout << "请输入您想删除的职工对应编号:";
cin >> id;
int index = this->employeePos(id);
if (index != -1)
{
//删除--数据前移,从需删除位置后一个开始前移
for (int i = index; i < this->m_EmpNum-1; i++)
{
//前移--由于是最终会将i+1的位置前移,所以为避免数组溢出,i+1应<this->m_EMpNum
this->m_EmpArray[i] = this->m_EmpArray[i+1];
}
//更新删除后职工的总数
this->m_EmpNum--;
//更新文件中保存的职工--对数据重新写入新文件
this->saveInfoToFile();
//删除完毕
cout << "删除成功!" << endl;
//Bug 当删除最后一个信息时,要将文件空判断变为true,
//避免删除最后一条职工信息后使变为空文件时,使用其他功能不显示用户提示语就按任意键继续
if (this->m_EmpNum == 0)
{
//文件空判断置为真
this->fileEmpty = true;
}
}
else
{
//未找到
cout << "不含该职工编号以及相关信息,删除失败!" << endl;
}
}
//暂停和清屏
system("pause");
system("cls");
}
//修改职工数据
void WorkerManager::modifyEmployeeData()
{
//判断文件是否存在或记录是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件数据为空,无法删除!" << endl;
}
else
{
int id = 0;
cout << "请输入您想删除的职工对应编号:";
cin >> id;
int index = this->employeePos(id);
if (index != -1)
{
//创建存放新数据的变量
int newId = 0;
string newName;
int newSelectDeptId = 0;
cout << "编号为 " << id << " 的职工已找到!" << endl<<endl;
//展示需要修改的职工信息,便于用户对比修改
cout << "为方便您修改,以下将展示该职工的相关信息:" << endl;
this->m_EmpArray[index]->showInfo();
//然后释放原职工数据
delete this->m_EmpArray[index];
//临时存放修改数据
cout <<endl<< "请输入新的职工编号:";
cin >> newId;
cout << "请输入新的职工姓名:";
cin >> newName;
cout << "请输入新职工所在岗位编号:" << endl;
cout << "1、普通员工" << endl
<< "2、经理" << endl
<< "3、老板" << endl;
cin >> newSelectDeptId;
//根据选择的不同岗位创建不同岗位的职工空间
//创建临时存放职工的空间
Worker* worker = NULL;
//分支语句实现相应控件创建
switch (newSelectDeptId)
{
case 1:
worker = new Employee(newId, newName, newSelectDeptId);
break;
case 2:
worker = new Manager(newId, newName, newSelectDeptId);
break;
case 3:
worker = new Boss(newId, newName, newSelectDeptId);
break;
default:
//异常退出处理
cout << "新职工部门编号输入有误!" << endl;
system("pause");
system("cls");
return;
}
//更新数组中对应的数据
this->m_EmpArray[index] = worker;
cout << "修改成功!" << endl;
//重新写入修改后的职工数组中的数据并保存到文件中
this->saveInfoToFile();
}
else
{
//不存在
cout << "该职工编号不存在,修改失败!" << endl;
}
}
//给用户留时间并清屏,提高界面的简洁性
system("pause");
system("cls");
}
//查找职工信息(据编号/姓名)
void WorkerManager::findEmployee()
{
//判断文件是否存在或记录是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件数据为空,无法删除!" << endl;
}
else
{
int select = 0;
cout << "请选择您的查找方式:" << endl;
cout << "1、按职工编号查找" << endl
<< "2、按职工姓名查找" << endl;
cin >> select;
if (select == 1) //按编号
{
int id = 0;
cout << "请输入您想查找的职工编号:";
cin >> id;
//判断该职工编号是否存在
int ret = this->employeePos(id);
if (ret != -1)
{
cout << "查找成功!" << endl;
cout << "以下是您查找的编号为 " << id << " 的职工的相关信息:" << endl;
this->m_EmpArray[ret]->showInfo();
}
else
{
cout << "查无此人,查找失败!" << endl;
}
}
if (select == 2) //按姓名
{
string name;
cout << "请输入您查找的职工姓名:" << endl;
cin >> name;
//判断是否存在该姓名的职工
bool is_ExitFindName = false;
for (int i = 0; i < this->m_EmpNum; ++i)
{
if (name == this->m_EmpArray[i]->m_Name)
{
//查找到则变为true
is_ExitFindName = true;
//打印找到的职工信息
cout << "查找成功!"<< "姓名为 " << name << " 的职工信息如下:" << endl;
//调用显示信息接口
cout << " "; //增加缩进,提高用户观感
//增加打印信息颜色,增强视觉效果
HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(outHandle, 0x06);
this->m_EmpArray[i]->showInfo();
SetConsoleTextAttribute(outHandle, 0x07);
}
}
if (is_ExitFindName == false)
{
cout << "未找到相应姓名的职工,查找失败!" << endl;
}
}
}
//暂停和清屏
system("pause");
system("cls");
}
//职工排序(选择排序实现)
void WorkerManager::employeeSort()
{
//判断文件是否存在或文件是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件为空,排序失败!" << endl;
system("pause");
system("cls");
return;
}
cout << "请选择您想要的排序方式:" << endl;
cout << "1、按编号升序排列" << endl
<< "2、按编号降序排列" << endl;
int select = 0, sign = 3;
//输入异常处理
while (sign)
{
cin >> select;
if ((select != 1) && (select != 2))
{
cout << "输入选项不存在!" << endl;
sign--;
if (sign == 0)
{
cout << "很抱歉,您输入错误次数过多,已无法进行选择!" << endl;
system("pause");
system("cls");
return;
}
cout << "您还有" << sign << "次机会选择," << "请重新输入:";
continue;
}
break;
}
//选择排序
for (int i = 0; i < this->m_EmpNum; i++)
{
//设置默认最小值或最大值
int minOrMax = i;
for (int j = i + 1; j < this->m_EmpNum; j++)
{
if (select == 1) //升序
{
//如果后面的职工编号 小于 默认最小编号,
//则将后一个职工位置作为默认编号最小的位置
if (this->m_EmpArray[minOrMax]->Id > this->m_EmpArray[j]->Id)
{
minOrMax = j;
}
}
else //降序
{
//与上述同理
if (this->m_EmpArray[minOrMax]->Id < this->m_EmpArray[j]->Id)
{
minOrMax = j;
}
}
}
//判断第i轮比较后原最小编号位置是否变化,若变化则交换相应职工信息
if (i != minOrMax)
{
//交换信息
Worker* temp = this->m_EmpArray[i];
this->m_EmpArray[i] = this->m_EmpArray[minOrMax];
this->m_EmpArray[minOrMax] = temp;
}
}
//排序完成
cout << "排序成功!排序后职工信息如下:" << endl;
//重写并保存排序后的职工信息至文件中
this->saveInfoToFile();
//显示排序后的职工信息
this->showEmployee(); //由于显示信息函数中已经附加暂停和清屏功能,所以此时无需自加了
}
//清空文件数据
void WorkerManager::clearFileData()
{
//判断文件是否存在或是否为空
if (this->fileEmpty)
{
cout << "文件不存在或文件为空,清空失败!" << endl;
}
else
{
cout << "您确定要清空吗?" << endl;
cout << "1、确定清空!" << endl << "2、不清空了,返回" << endl;
int select = 0;
cin >> select;
//清空
if (select == 1)
{
//清空文件----删除文件并重新创建一个空文件
ofstream ofs(FILENAME, ios::trunc);
//释放存放职工的所有空间
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
//释放并置空各个数组元素并置空
if (this->m_EmpArray[i] != NULL)
{
delete this->m_EmpArray[i];
this->m_EmpArray[i] = NULL;
}
}
//职工数组空间释放并置空
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
//职工人数置为0
this->m_EmpNum = 0;
//文件清空后,判断文件为空的条件置为true
this->fileEmpty = true;
cout << "清空成功!" << endl;
}
}
}
system("pause");
system("cls");
}
//退出系统
void WorkerManager::exitSystem()
{
cout << "欢迎下次使用" << endl;
system("pause");
exit(0);
}
WorkerManager::~WorkerManager()
{
//堆区内存手动开辟手动释放
if (this->m_EmpArray != NULL)
{
//将每一个元素均释放
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i] != NULL)
{
//释放
delete this->m_EmpArray[i];
}
}
//内部元素释放完毕后再把整个空间均释放,并且置空
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
}
}
2、职工管理类头文件(包含所有功能函数声明以及相关依赖项)
创建职工管理类,在类中定义其构造函数和析构函数,以搭建大致框架。接着陆续向其中加入功能函数声明以及相关依赖项。
#pragma once //防止头文件重复编译
#include<iostream>
#include<cstdlib>
#include"Worker.h"
#include"Employee.h"
#include"Manager.h"
#include"Boss.h"
#include<fstream> //文件流
#include<windows.h> //方便查找出的职工信息显示高亮
#define FILENAME "EmployeeInfoFile.txt"
using namespace std;
class WorkerManager
{
public:
//构造函数
WorkerManager();
//菜单展示
void showManu();
/*
添加职工人数
根据多态的特性,创建抽象类类型数组存放基类指针以间接存放新加职工,
并用m_EmpNum记录已有职工人数,添加时利用newNum记录需要新增的人数,
创建新空间存放新增后的总职工,并用Switch语句实现用户选择添加职工的部门岗位,
接着释放原本职工数组空间,将新空间赋予职工数组以及新增后的职工人数赋予原职工人数
*/
//记录职工人数
int m_EmpNum;
//创建职工指针
Worker** m_EmpArray;
//添加职工信息(批量)
void addEmp();
/*
添加并职工信息后,为了能方便查看和永久保存,我们将创建一个文件记录和保存职工信息
*/
//保存文件
void saveInfoToFile();
/*
写入文件后,但每次打开程序都会将数据初始化导致数据被清空,以至于无法后期
查看已有职工信息。其中,读文件时,有三种情况:
1、第一次记录数据,文件还未创建
2、已经存在文件,但是文件中的数据被清空了
3、文件已经存在,且保存了所有添加的职工信息的数据
*/
//判断文件是否为空
bool fileEmpty;
//记录已有职工人数
int getEmpSum();
//初始化职工
void initEmployee();
//显示职工
void showEmployee();
/*
按编号删除职工信息,首先判断文件是否存在以及文件是否为空,
再判断文件中是否存在该职工编号,若存在则执行删除操作
对需要删除数据后面所有数据前移,覆盖需删数据并使职工总数-1,
接着重新写入并保存删除职工后的数据,
使得最终达到逻辑上的职工数据删除操作。
*/
//判断某职工是否存在,存在则返回位置,不存在返回-1
int employeePos(int id);
//按编号删除职工
void delEmployee();
//修改职工数据
void modifyEmployeeData();
//查找职工信息(据编号/姓名)
void findEmployee();
//职工排序
void employeeSort();
//清空文件数据
void clearFileData();
//退出系统
void exitSystem();
//析构函数
~WorkerManager();
};
三、主调函数实现
框架搭建,调用功能填补。
#include<iostream>
#include"WorkManager.h"
using namespace std;
int main()
{
WorkerManager wm;
//showTest();
while (1)
{
wm.showManu();
int choice;
cout << "请输入您的选择:";
cin >> choice;
switch (choice)
{
case 1: //增加职工信息
wm.addEmp();
break;
case 2: //显示职工信息
wm.showEmployee();
break;
case 3: //删除职工信息
wm.delEmployee();
break;
case 4: //修改职工信息
wm.modifyEmployeeData();
break;
case 5: //查找职工信息
wm.findEmployee();
break;
case 6: //按照编号排序
wm.employeeSort();
break;
case 7: //清空所有文档
wm.clearFileData();
break;
case 0: //退出管理程序
wm.exitSystem();
break;
default:
cout << "输入有误,请重新输入!" << endl;
system("pause");
system("cls");
break;
}
}
return 0;
}
信息展示测试和判断职工是否存在测试代码如下:
//展示信息测试
void showTest()
{
//员工
Worker* worker1 = NULL;
worker1 = new Employee(1, "张三", 1);
//输出
worker1->showInfo();
delete worker1;//经理
Worker* worker2 = NULL;
worker2 = new Manager(2, "李四", 2);
//输出
worker2->showInfo();
delete worker2;//老板
Worker* worker3 = NULL;
worker3 = new Employee(3, "王五", 3);
//输出
worker3->showInfo();
delete worker3;
}//判断存在的测试
void isExitPos_Test(WorkerManager wm)
{
int ret = wm.employeePos(1);
if (ret != -1)
{
cout << "职工存在!" << endl;
}
else
{
cout << "职工不存在!" << endl;
}
}
五、完整代码获取
由于本文内容已经较大,且本程序是分文件处理编写,因此就不在这放了,如需源码,评论区私信即可。谢谢!
最后,上述讲解难以避免有一些纰漏或不足之处,欢迎小伙伴们在评论区批评指正或留言~