我的第一个C++千行项目——职工管理系统,宣布完结!
项目介绍
这是一个面向C++学习者的一个小项目,很适合已经学习完C++核心编程的朋友来练手,它可以帮助你把之前学习的技能融会贯通。
它的主要功能为:
- 增加职工信息
- 显示职工信息
- 删除离职职工
- 修改职工信息
- 查找职工信息
- 按照编号排序
- 清空所有文档
了解了需求之后,用你的方式去实现它吧!
以下是我的实现:
头文件
boss.h
#pragma once
#include "worker.h"
class Boss :public Worker
{
public:
Boss(int workerId, string name);
//显示个人所有信息
void showInfo();
//获取员工名字
string getWorkerName();
//获取岗位
string getDepartmentName();
//获取职责
string getDepartmentDuty();
};
employee.h
#pragma once
#include "worker.h"
class Employee :public Worker
{
public:
Employee(int workerId, string name);
//显示个人所有信息
void showInfo();
//获取员工名字
string getWorkerName();
//获取岗位
string getDepartmentName();
//获取职责
string getDepartmentDuty();
};
manager.h
#pragma once
#include "worker.h"
class Manager:public Worker
{
public:
Manager(int workerId, string name);
//显示个人所有信息
void showInfo();
//获取员工名字
string getWorkerName();
//获取岗位
string getDepartmentName();
//获取职责
string getDepartmentDuty();
};
worker.h
#pragma once
#include <iostream>
using namespace std;
#include <string>
class Worker
{
public:
//显示个人信息
virtual void showInfo() = 0;
//获取员工名字
virtual string getWorkerName() = 0;
//获取岗位
virtual string getDepartmentName() = 0;
//获取职责
virtual string getDepartmentDuty() = 0;
//职工编号
int workerId;
//职工名字
string workerName;
//岗位
string departmentName;
//职责
string departmentDuty;
};
workerManager.h
#pragma once
#include <iostream>
#include <fstream>
#include "worker.h"
using namespace std;
#define FILENAME "workerFile.txt"
//适配开始菜单的enum
enum
{
EXIT,
ADD,
DISPALY,
DELETE,
MODIFY,
SEEK,
SORT,
EMPTY
};
//适配员工职务选择的enum
enum
{
BOSS = 1,
MANAGER,
EMPLOYEE
};
//适配查找员工方式的enum
enum
{
RETURN,
BYID,
BYNAME
};
//启动程序
void Start();
//管理与用户的交互,调用职工管理系统内核
class WorkerManager
{
public:
WorkerManager();
void showMenu();
void exitSystem();
~WorkerManager();
void AddWorker();
void saveToFile();
//从程序外的文件中,展示职工信息
void showFromFile();
//从程序内的数组中,展示职工信息
void showFromArray();
//初始化数组
void initArray();
//判断员工是否存在
// 1.存在 -> 返回其下标
// 2.不存在 -> 返回 -1
int isExist(int id);
//删除员工
void deleteWorker();
//修改员工信息
void modifyWorkerInfo();
void seekWorker();
int getWorkerNumber();
void sortWorkerById();
bool existInfo();
void emptyInfo();
private:
bool fileEmpty;
int fileWorkerNumber;
int workerNumber;
Worker** workerArray;
};
源文件
boss.cpp
#include "boss.h"
Boss::Boss(int workerId, string name)
{
this->workerId = workerId;
this->workerName = name;
this->departmentName = "老板";
this->departmentDuty = "为公司指明发展方向";//因为所有boss的职责都一样,所以我们这里直接写死
}
//显示个人所有信息
void Boss::showInfo()
{
cout << "职工编号: " << this->workerId
<< " \t职工姓名: " << this->workerName
<< " \t岗位:" << this->getDepartmentName()
<< " \t岗位职责:" << this->getDepartmentDuty() << endl;
}
//获取员工名字
string Boss::getWorkerName()
{
return this->workerName;
}
//获取岗位
string Boss::getDepartmentName()
{
return this->departmentName;
}
//获取职责
string Boss::getDepartmentDuty()
{
return this->departmentDuty;
}
employee.cpp
#include "workerManager.h"
#include "boss.h"
#include "employee.h"
#include "manager.h"
void Start()
{
WorkerManager wm;
int choice = 0;
while (true)
{
wm.showMenu();
cin >> choice;
switch (choice)
{
case EXIT:
wm.exitSystem();
break;
case ADD:
wm.AddWorker();
wm.saveToFile();
break;
case DISPALY:
//wm.showFromFile();
wm.showFromArray();
break;
case DELETE:
wm.deleteWorker();
break;
case MODIFY:
wm.modifyWorkerInfo();
break;
case SEEK:
wm.seekWorker();
break;
case SORT:
wm.sortWorkerById();
break;
case EMPTY:
wm.emptyInfo();
break;
default:
cout << "输入错误,请重新输入数字0~7:" << endl;
cout << endl;
break;
}
}
}
void WorkerManager::showMenu()
{
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;
}
WorkerManager::WorkerManager()
{
//文件打开有3种情况,我们要为其做3种不同的构造函数:
// 1.没有文件
// 2.有文件,但内容为空(上次使用时,最后一步操作是“清空所有文档”)
// 3.有文件,且里面有内容
//
ifstream ifs;
ifs.open(FILENAME, ios::in);
char fileEnd;
//1.没有文件
if (!ifs.is_open())
{
//cout << "文件不存在" << endl;
this->fileEmpty = true;
this->workerNumber = 0;
this->workerArray = NULL;
this->fileWorkerNumber = 0;
ifs.close();
return;
}
//2.有文件,但内容为空
//
//文件中最少会有一个字符——EOF,现在我们从文件读走一个字符
//如果文件因此为空了,那么说明我们刚刚读走的就是EOF,也就是说,现在文档里什么都没有了
//然后,我们利用ifs.eof()去判断文件是否为空,
// 若为空,则得出结论:该文件内容为空
ifs >> fileEnd;
if (ifs.eof())
{
//cout << "文件为空" << endl;
this->fileEmpty = true;
this->workerNumber = 0;
this->workerArray = NULL;
this->fileWorkerNumber = 0;
ifs.close();
return;
}
//3.有文件,且里面有内容
//
//当代码走到这,说明前2种情况都没有发生,所以此处不需要进行if判断
//a.先求出文件中有几条数据
this->fileWorkerNumber = this->getWorkerNumber();
//cout << this->fileWorkerNumber;
//b.在数据中开辟等量的空间,以便后续将文件中的数据存放至数组中
this->workerArray = new Worker * [this->fileWorkerNumber];
//c.将文件中的数据存放至数组中
initArray();
this->fileEmpty = false;//更新文件是否为空的标志
//d.更新数组中当前的数据数量
this->workerNumber = this->fileWorkerNumber;
//测试代码
//for (int i = 0; i < this->workerNumber; i++)
//{
// cout << this->workerArray[i]->workerId << " "
// << this->workerArray[i]->workerName << " "
// << this->workerArray[i]->departmentName << " "
// << this->workerArray[i]->departmentDuty << endl;
//}
}
WorkerManager::~WorkerManager()
{
int i = 0;
//我们这里是2级指针,所有实质上我们需要释放2次,才可以释放干净空间
//1.释放数组中,每个元素(实质上是一级指针)所指向的空间
if (this->workerArray != NULL)
{
for (i = 0; i < this->workerNumber; i++)
{
if (*(this->workerArray + i) != NULL)
{
delete* (this->workerArray + i);
*(this->workerArray + i) = NULL;
}
}
}
//2.释放数组(释放数组中的每一个一级指针)
delete[] this->workerArray;
this->workerArray = NULL;
}
int WorkerManager::getWorkerNumber()
{
ifstream ifs;
int count = 0;
string buf;
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "getWorkerNumber::打开文件失败!" << endl;
return -1;
}
//getline函数,可以一次读一行
//而我们一行只放了一个职工,所以行数 == 职工数
while (getline(ifs, buf))
{
count++;
}
ifs.close();
return count;
}
void WorkerManager::initArray()
{
ifstream ifs;
int workerId = 0;
string workerName;
string departmentDuty;
string departmentName;
int i = 0;
Worker* newWorker = NULL;
//打开文件,以便进行后续读取数据操作
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "initArray::文件打开失败" << endl;
}
//把文件中的数据拿出来,存放入程序的数组中
while (ifs >> workerId && ifs >> workerName && ifs >> departmentName && ifs >> departmentDuty)
{
if (departmentName == "老板")
{
newWorker = new Boss(workerId, workerName);
}
else if (departmentName == "经理")
{
newWorker = new Manager(workerId, workerName);
}
else//普通员工
{
newWorker = new Employee(workerId, workerName);
}
*(this->workerArray + i) = newWorker;
i++;
}
//关闭文件
ifs.close();
}
void WorkerManager::AddWorker()
{
//分配思想:有一个数据,分配一个空间
int addNumber = 0;
int newNumber = 0;
Worker** newSpace = NULL;
Worker* newWorker = NULL;
int i = 0;
int idCheck = 0;
bool existSameId = false;
string newName;
int newDepartment = 0;
int newWorkerId = 0;
//录入需要的新空间数量
while (true)
{
cout << "请输入增加职工的数量:" << endl;
cin >> addNumber;
//合法性判断:不可以是零或负数
if (addNumber <= 0)
{
cout << "非法输入!请输入正整数" << endl;
}
else
{
break;
}
}
newNumber = addNumber + this->workerNumber;
//开始分配空间
newSpace = new Worker * [newNumber];
if (newSpace == NULL)
{
cout << "AddWorker::内存分配失败!" << endl;
exit(1);
}
//开始往空间中存入数据
//
// 什么时候需要存数据?在有数据的时候。
// 所以我们这里直接判断workerNumber是否有值
//
//先把之前的数据转移至新空间中
if (this->workerNumber > 0)
{
for (i = 0; i < this->workerNumber; i++)
{
*(newSpace + i) = *(workerArray + i);
}
}
//录入新数据
for (i = 0; i < addNumber; i++)
{
cout << "请输入第" << i + 1 << "个新员工的姓名:" << endl;
cin >> newName;
//员工ID为主码,具有唯一性,所以我们必须检查是否有相同的ID录入
//如果有,我们要求重新输入ID
//
//检查方法:遍历所有已存在的员工ID + 新增的员工ID(此ID还未拼接至程序的数组中),一一比对
// (后续可以做先排序,再二分查找的优化,不过目前暂时不需要)
cout << "请输入第" << i + 1 << "个新员工的工号:" << endl;
do
{
existSameId = false;//每次进入循环时,重置标志为:不存在相同ID
cin >> newWorkerId;
//检查已存在的员工ID
for (idCheck = 0; idCheck < this->workerNumber; idCheck++)
{
if ((*(this->workerArray + idCheck))->workerId == newWorkerId)
{
cout << "ID号" << newWorkerId << "已存在, 请重新输入员工ID:" << endl;
existSameId = true;
break;
}
}
//检查本次新增的员工ID
if (addNumber > 1)
{
for (idCheck = 0; idCheck < i; idCheck++)
{
if ((*(newSpace + idCheck))->workerId == newWorkerId)
{
cout << "ID号" << newWorkerId << "已存在, 请重新输入员工ID:" << endl;
existSameId = true;
break;
}
}
}
} while (existSameId);
cout << "请输入第" << i + 1 << "个新员工的职务:" << endl;
cout << "1.老板" << endl;
cout << "2.经理" << endl;
cout << "3.普通员工" << endl;
cin >> newDepartment;
switch (newDepartment)
{
case BOSS:
newWorker = new Boss(newWorkerId, newName);
break;
case MANAGER:
newWorker = new Manager(newWorkerId, newName);
break;
case EMPLOYEE:
newWorker = new Employee(newWorkerId, newName);
break;
default:
cout << "输入错误!请在职务选择处,输入数字 1~3 的闭区间:" << endl;
i--;
break;
}
*(newSpace + this->workerNumber + i) = newWorker;
}
//成功提示
cout << "成功添加" << i << "名新职工" << endl;
this->fileEmpty = false;//更新文件标志,表示文件不再为空
//释放原来的空间
//delete[] this->workerArray;
for (i = 0; i < this->workerNumber; i++)
{
if (*(this->workerArray + i) != NULL)
{
delete* (this->workerArray + i);
*(this->workerArray + i) = NULL;
}
}
this->workerArray = newSpace;
this->workerNumber = newNumber;
system("pause");
system("cls");
}
void WorkerManager::deleteWorker()
{
//1.判断文件中是否有数据
// 1.1 没有数据 -> 向屏幕输出报错信息(没有数据无法进行删除操作)
// 1.2 有数据 -> 进行步骤2
//2.要求用户输入ID
// 2.1 没有此员工 -> 向屏幕输出报错信息:"没有该员工"
// 2.2 有此员工 -> 在数组中删除此员工,并将数组的情况更新至文件中
if (!this->existInfo())
{
return;
}
int i = 0;
int id = 0;
int index = 0;
cout << "请输入要删除职工的ID:" << endl;
cin >> id;
index = this->isExist(id);
if (index == -1)
{
cout << "没有ID为" << id << "的员工" << endl;
}
else
{
//删除数组中,下标为index的元素
for (i = index; i < this->workerNumber - 1; i++)
{
//该数组中,本质上存放的是Worker类型的一级指针,所以可以直接赋值,而不需要重写等号运算符
this->workerArray[i] = this->workerArray[i + 1];
}
//更新数组中的元素数量
this->workerNumber--;
//将数组中的情况,更新至文件中
this->saveToFile();
//向用户输出提示信息
cout << "删除成功!" << endl;
}
system("pause");
system("cls");
}
void WorkerManager::saveToFile()
{
ofstream ofs;
int i = 0;
ofs.open(FILENAME, ios::out);
for (i = 0; i < this->workerNumber; i++)
{
ofs << this->workerArray[i]->workerId << " "
<< this->workerArray[i]->workerName << " "
<< this->workerArray[i]->departmentName << "\t"
<< this->workerArray[i]->departmentDuty << endl;
}
ofs.close();
}
void WorkerManager::showFromFile()
{
ifstream ifs;
string buf;
//当我们尝试去打开一个文件时,有这几种情况:
// 1.有这个文件 -> 打开成功
// 2.没有这个文件 -> 打开失败
//
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "showFromFile::文件打开失败" << endl;
return;
}
//将文件的内容输出到屏幕上
while (getline(ifs, buf))
{
cout << buf << endl;
}
ifs.close();
system("pause");
system("cls");
}
int WorkerManager::isExist(int id)
{
//我们先不考虑id同名的情况,暂时认为每一个职工ID都是唯一的
//在此前提的基础上,我们查找某一个ID的下标,则只需要遍历一遍数组即可
int i = 0;
for (i = 0; i < this->workerNumber; i++)
{
if (this->workerArray[i]->workerId == id)
{
//找到该员工,直接返回下标
return i;
}
}
//如果程序走到这儿,说明没有该员工。返回 -1
return -1;
}
void WorkerManager::modifyWorkerInfo()
{
//修改员工函数实现思想:
// 1.合法性判断 -> 判断是否有该员工 || 文件里是否有数据 (这2种情况应该输出不同的提示)
// 1.1 没有 -> 提示用户没有该用户,退出函数
// 1.2 有 -> 进行步骤2
// 2.将此员工delete掉
// 3.把新数据存入该空间
if (!this->existInfo())
{
return;
}
bool flag = true;
int idWantModify = 0;
int indexOfIdWantModify = 0;
//员工的新信息
Worker* newInfo = NULL;
int newId = 0;
string newName = "";
int newDepartment = 0;
cout << "请问你要修改的员工的ID是:" << endl;
cin >> idWantModify;
//判断是否存在此员工
indexOfIdWantModify = isExist(idWantModify);
if (indexOfIdWantModify == -1)
{
cout << "该用户不存在" << endl;
system("pause");
system("cls");
return;
}
else
{
delete this->workerArray[indexOfIdWantModify];
cout << "请输入原员工ID为" << idWantModify << "的新ID:" << endl;
cin >> newId;
cout << "请输入新名字:" << endl;
cin >> newName;
cout << "请输入新职务:" << endl;
cout << "1.老板" << endl;
cout << "2.经理" << endl;
cout << "3.普通员工" << endl;
cin >> newDepartment;
while (flag)//在选择处添加一个循环,这样在错误输入时就可以重新输入
{
switch (newDepartment)
{
case BOSS:
newInfo = new Boss(newId, newName);
flag = false;
break;
case MANAGER:
newInfo = new Manager(newId, newName);
flag = false;
break;
case EMPLOYEE:
newInfo = new Employee(newId, newName);
flag = false;
break;
default:
cout << "输入错误!请在职务选择处,输入数字 1~3 的闭区间:" << endl;
break;
}
}
cout << "修改成功!" << endl;
*(this->workerArray + indexOfIdWantModify) = newInfo;//将新数据存入程序数组
this->saveToFile();//更新文件信息
system("pause");
system("cls");
}
}
void WorkerManager::seekWorker()
{
//实现思想:
// 1.合法性判断
// 2.提供两种查找方式
// 2.1 按ID查找 -> 输出此ID的员工信息
// 2.2 按姓名查找 -> 输出所有拥有该姓名的员工 (考虑到同名情况,我们应该遍历所有员工)
//
if (!this->existInfo())
{
return;
}
int seekBy = 0;
int idWantSeek = 0;
int i = 0;
int indexIdWantSeek = 0;
string nameWantSeek = "";
bool ExistTheWorker = false;
cout << "0.返回上一层" << endl;
cout << "1.按员工ID查找" << endl;
cout << "2.按员工姓名查找" << endl;
while (true)
{
cin >> seekBy;
//合法性判断
if (seekBy != 1 && seekBy != 2 && seekBy != 0)
{
cout << "输入错误!请输入 1, 2 或 0:" << endl;
}
else
{
break;
}
}
switch (seekBy)
{
case RETURN://退出修改函数
cout << "按任意键返回上一菜单" << endl;
break;
case BYID:
{
//1.判断该员工是否存在
// 1.1 存在 -> 输出该员工信息
// 1.2 不存在 -> 退出函数,返回主菜单
cout << "请输入要查找的员工ID:" << endl;
cin >> idWantSeek;
indexIdWantSeek = this->isExist(idWantSeek);
if (indexIdWantSeek != -1)
{
//this->workerArray[indexIdWantSeek]->showInfo();
(*(this->workerArray + indexIdWantSeek))->showInfo();
}
else
{
cout << "没有该员工" << endl;
}
}
break;
case BYNAME:
{
cout << "请输入要查找的员工姓名:" << endl;
cin >> nameWantSeek;
for (i = 0; i < this->workerNumber; i++)
{
if ((*(this->workerArray + i))->workerName == nameWantSeek)
{
(*(this->workerArray + i))->showInfo();
ExistTheWorker = true;
}
}
//遍历结束,没有该员工
if (!ExistTheWorker)
{
cout << "没有姓名为 " << nameWantSeek << " 的员工" << endl;
}
}
break;
default:
break;
}
system("pause");
system("cls");
}
void WorkerManager::sortWorkerById()
{
//实现思想:利用冒泡排序继续
//
if (!this->existInfo())
{
return;
}
int sortStyle = 0;
int i = 0;
int j = 0;
Worker* temp = NULL;
bool swapped = false;
cout << "请选择排序方式:" << endl;
cout << "1.升序" << endl;
cout << "2.降序" << endl;
while (true)
{
//录入用户的选择
cin >> sortStyle;
//合法性判断
if (cin.fail() || sortStyle != 1 && sortStyle != 2)
{
cout << "输入错误,请重新输入 1 或 2 :" << endl;
// 清除错误标志
cin.clear();
// 忽略掉剩余的输入(直到换行符)
cin.ignore(numeric_limits < streamsize >::max(), '\n');
}
else
{
for (i = 0; i < this->workerNumber; i++)
{
for (j = 0; j < this->workerNumber - i - 1; j++)
{
if (sortStyle == 1)
{
//升序 (从小到大)
if ((*(this->workerArray + j))->workerId > (*(this->workerArray + j + 1))->workerId)
{
temp = *(this->workerArray + j);
*(this->workerArray + j) = *(this->workerArray + j + 1);
*(this->workerArray + j + 1) = temp;
swapped = true;
}
}
else
{
//降序 (从大到小)
if ((*(this->workerArray + j))->workerId < (*(this->workerArray + j + 1))->workerId)
{
temp = *(this->workerArray + j);
*(this->workerArray + j) = *(this->workerArray + j + 1);
*(this->workerArray + j + 1) = temp;
swapped = true;
}
}
}
//本次循环没有发生排序行为,即表示当前数据已经有序,提前退出循环
if (!swapped)
{
break;
}
}
//走到这里,说明输入正确,且排序结束
//
// 最后收尾工作:
// 1.输出排序成功提示,
// 2.更新至文档
// 3.跳出循环,结束函数
cout << "排序成功!" << endl;
this->saveToFile();
system("pause");
system("cls");
break;
}
}
}
void WorkerManager::emptyInfo()
{
if (!this->existInfo())
{
return;
}
//实现思想:
// 1. 清空文件信息:删除文件,并创建同名空文件
// 2. 清空程序中数组信息:delete所有workerArrat中的数据
int i = 0;
int doubleConfirmation = 0;
ofstream ofs;
//清空功能比较危险,所以要求用户进行二次确认
cout << "确认清空所有数据吗?" << endl;
cout << "1.确认" << endl;
cout << "2.返回主菜单" << endl;
cin >> doubleConfirmation;
//如果用户输入的是字符或其他任何非 1 的数据,都会直接返回主菜单
if (doubleConfirmation == 1)
{
//清空文件
//这个命令会判断文件是否存在,如果存在,它将删除此文件,并新创建一个同名空文件
//换句话说,它可以实现清空文件内容的功能
ofs.open(FILENAME, ios::trunc);
ofs.close();
//清空数组
if (this->workerArray != NULL)
{
for (i = 0; i < this->workerNumber; i++)
{
if (this->workerArray + i != NULL)
{
delete* (this->workerArray + i);
*(this->workerArray + i) = NULL;
}
}
delete[] this->workerArray;
this->workerArray = NULL;
this->fileEmpty = true;
this->workerNumber = 0;
this->fileWorkerNumber = 0;
}
cout << "清空成功!" << endl;
}
system("pause");
system("cls");
}
void WorkerManager::exitSystem()
{
cout << "已经退出系统,欢迎下次使用" << endl;
exit(0);
}
bool WorkerManager::existInfo()
{
//为什么要设置2个条件?
//因为在这样一种情况时,fileEmpty为false,但实质上却已经没有数据了:打开系统后,利用删除功能,把用户一个个删完
if (this->workerNumber == 0 || this->fileEmpty == true)
{
cout << "文件不存在或文件为空" << endl;
system("pause");
system("cls");
return false;
}
return true;
}
void WorkerManager::showFromArray()
{
// 1.判断数组是否有值
// 1.1 若数组不存在 -> 输出报错信息:“文件不存在或文件为空”
// 1.2 若数组有值 -> 输出数组信息
int i = 0;
if (!this->existInfo())
{
return;
}
else
{
for (i = 0; i < this->workerNumber; i++)
{
this->workerArray[i]->showInfo();
}
}
system("pause");
system("cls");
}
manager.cpp
#include "manager.h"
Manager::Manager(int workerId, string name)
{
this->workerId = workerId;
this->workerName = name;
this->departmentName = "经理";
this->departmentDuty = "将任务合理分解给员工";//因为所有employee的职责都一样,所以我们这里直接写死
}
//显示个人所有信息
void Manager::showInfo()
{
cout << "职工编号: " << this->workerId
<< " \t职工姓名: " << this->workerName
<< " \t岗位:" << this->getDepartmentName()
<< " \t岗位职责:" << this->getDepartmentDuty() << endl;
}
//获取员工名字
string Manager::getWorkerName()
{
return this->workerName;
}
//获取岗位
string Manager::getDepartmentName()
{
return this->departmentName;
}
//获取职责
string Manager::getDepartmentDuty()
{
return this->departmentDuty;
}
workerManager.cpp
#include "workerManager.h"
#include "boss.h"
#include "employee.h"
#include "manager.h"
void Start()
{
WorkerManager wm;
int choice = 0;
while (true)
{
wm.showMenu();
cin >> choice;
switch (choice)
{
case EXIT:
wm.exitSystem();
break;
case ADD:
wm.AddWorker();
wm.saveToFile();
break;
case DISPALY:
//wm.showFromFile();
wm.showFromArray();
break;
case DELETE:
wm.deleteWorker();
break;
case MODIFY:
wm.modifyWorkerInfo();
break;
case SEEK:
wm.seekWorker();
break;
case SORT:
wm.sortWorkerById();
break;
case EMPTY:
wm.emptyInfo();
break;
default:
cout << "输入错误,请重新输入数字0~7:" << endl;
cout << endl;
break;
}
}
}
void WorkerManager::showMenu()
{
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;
}
WorkerManager::WorkerManager()
{
//文件打开有3种情况,我们要为其做3种不同的构造函数:
// 1.没有文件
// 2.有文件,但内容为空(上次使用时,最后一步操作是“清空所有文档”)
// 3.有文件,且里面有内容
//
ifstream ifs;
ifs.open(FILENAME, ios::in);
char fileEnd;
//1.没有文件
if (!ifs.is_open())
{
//cout << "文件不存在" << endl;
this->fileEmpty = true;
this->workerNumber = 0;
this->workerArray = NULL;
this->fileWorkerNumber = 0;
ifs.close();
return;
}
//2.有文件,但内容为空
//
//文件中最少会有一个字符——EOF,现在我们从文件读走一个字符
//如果文件因此为空了,那么说明我们刚刚读走的就是EOF,也就是说,现在文档里什么都没有了
//然后,我们利用ifs.eof()去判断文件是否为空,
// 若为空,则得出结论:该文件内容为空
ifs >> fileEnd;
if (ifs.eof())
{
//cout << "文件为空" << endl;
this->fileEmpty = true;
this->workerNumber = 0;
this->workerArray = NULL;
this->fileWorkerNumber = 0;
ifs.close();
return;
}
//3.有文件,且里面有内容
//
//当代码走到这,说明前2种情况都没有发生,所以此处不需要进行if判断
//a.先求出文件中有几条数据
this->fileWorkerNumber = this->getWorkerNumber();
//cout << this->fileWorkerNumber;
//b.在数据中开辟等量的空间,以便后续将文件中的数据存放至数组中
this->workerArray = new Worker * [this->fileWorkerNumber];
//c.将文件中的数据存放至数组中
initArray();
this->fileEmpty = false;//更新文件是否为空的标志
//d.更新数组中当前的数据数量
this->workerNumber = this->fileWorkerNumber;
//测试代码
//for (int i = 0; i < this->workerNumber; i++)
//{
// cout << this->workerArray[i]->workerId << " "
// << this->workerArray[i]->workerName << " "
// << this->workerArray[i]->departmentName << " "
// << this->workerArray[i]->departmentDuty << endl;
//}
}
WorkerManager::~WorkerManager()
{
int i = 0;
//我们这里是2级指针,所有实质上我们需要释放2次,才可以释放干净空间
//1.释放数组中,每个元素(实质上是一级指针)所指向的空间
if (this->workerArray != NULL)
{
for (i = 0; i < this->workerNumber; i++)
{
if (*(this->workerArray + i) != NULL)
{
delete* (this->workerArray + i);
*(this->workerArray + i) = NULL;
}
}
}
//2.释放数组(释放数组中的每一个一级指针)
delete[] this->workerArray;
this->workerArray = NULL;
}
int WorkerManager::getWorkerNumber()
{
ifstream ifs;
int count = 0;
string buf;
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "getWorkerNumber::打开文件失败!" << endl;
return -1;
}
//getline函数,可以一次读一行
//而我们一行只放了一个职工,所以行数 == 职工数
while (getline(ifs, buf))
{
count++;
}
ifs.close();
return count;
}
void WorkerManager::initArray()
{
ifstream ifs;
int workerId = 0;
string workerName;
string departmentDuty;
string departmentName;
int i = 0;
Worker* newWorker = NULL;
//打开文件,以便进行后续读取数据操作
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "initArray::文件打开失败" << endl;
}
//把文件中的数据拿出来,存放入程序的数组中
while (ifs >> workerId && ifs >> workerName && ifs >> departmentName && ifs >> departmentDuty)
{
if (departmentName == "老板")
{
newWorker = new Boss(workerId, workerName);
}
else if (departmentName == "经理")
{
newWorker = new Manager(workerId, workerName);
}
else//普通员工
{
newWorker = new Employee(workerId, workerName);
}
*(this->workerArray + i) = newWorker;
i++;
}
//关闭文件
ifs.close();
}
void WorkerManager::updateArray(Worker** newArray)
{
this->workerArray = newArray;
}
void WorkerManager::updateWorkerNumber(int newWorkerNumber)
{
this->workerNumber = newWorkerNumber;
}
void WorkerManager::AddWorker()
{
//有一个数据,分配一个空间
int addNumber = 0;
int newNumber = 0;
Worker** newSpace = NULL;
Worker* newWorker = NULL;
int i = 0;
int idCheck = 0;
bool existSameId = false;
string newName;
int newDepartment = 0;
int newWorkerId = 0;
//录入需要的新空间数量
while (true)
{
cout << "请输入增加职工的数量:" << endl;
cin >> addNumber;
//合法性判断:不可以是零或负数
if (addNumber <= 0)
{
cout << "非法输入!请输入正整数" << endl;
}
else
{
break;
}
}
newNumber = addNumber + this->workerNumber;
//开始分配空间
newSpace = new Worker * [newNumber];
if (newSpace == NULL)
{
cout << "AddWorker::内存分配失败!" << endl;
exit(1);
}
//开始往空间中存入数据
//
// 什么时候需要存数据?在有数据的时候。
// 所以我们这里直接判断workerNumber是否有值
//
//先把之前的数据转移至新空间中
if (this->workerNumber > 0)
{
for (i = 0; i < this->workerNumber; i++)
{
*(newSpace + i) = *(workerArray + i);
}
}
//录入新数据
for (i = 0; i < addNumber; i++)
{
cout << "请输入第" << i + 1 << "个新员工的姓名:" << endl;
cin >> newName;
//员工ID为主码,具有唯一性,所以我们必须检查是否有相同的ID录入
//如果有,我们要求重新输入ID
//
//检查方法:遍历所有已存在的员工ID + 新增的员工ID(此ID还为拼接至程序的数组中),一一比对
// (后续可以做先排序,再二分查找的优化,不过目前暂时不需要)
cout << "请输入第" << i + 1 << "个新员工的工号:" << endl;
do
{
existSameId = false;//每次进入循环时,重置标志为:不存在相同ID
cin >> newWorkerId;
//检查已存在的员工ID
for (idCheck = 0; idCheck < this->workerNumber; idCheck++)
{
if ((*(this->workerArray + idCheck))->workerId == newWorkerId)
{
cout << "ID号" << newWorkerId << "已存在, 请重新输入员工ID:" << endl;
existSameId = true;
break;
}
}
//检查本次新增的员工ID
if (addNumber > 1)
{
for (idCheck = 0; idCheck < i; idCheck++)
{
if ((*(newSpace + idCheck))->workerId == newWorkerId)
{
cout << "ID号" << newWorkerId << "已存在, 请重新输入员工ID:" << endl;
existSameId = true;
break;
}
}
}
} while (existSameId);
cout << "请输入第" << i + 1<< "个新员工的职务:" << endl;
cout << "1.老板" << endl;
cout << "2.经理" << endl;
cout << "3.普通员工" << endl;
cin >> newDepartment;
switch (newDepartment)
{
case BOSS:
newWorker = new Boss(newWorkerId, newName);
break;
case MANAGER:
newWorker = new Manager(newWorkerId, newName);
break;
case EMPLOYEE:
newWorker = new Employee(newWorkerId, newName);
break;
default:
cout << "输入错误!请在职务选择处,输入数字 1~3 的闭区间:" << endl;
i--;
break;
}
*(newSpace + this->workerNumber + i) = newWorker;
}
//成功提示
cout << "成功添加" << i << "名新职工" << endl;
this->fileEmpty = false;//更新文件标志,表示文件不再为空
//释放原来的空间
//delete[] this->workerArray;
for (i = 0; i < this->workerNumber; i++)
{
if (*(this->workerArray + i) != NULL)
{
delete *(this->workerArray + i);
*(this->workerArray + i) = NULL;
}
}
this->workerArray = newSpace;
this->workerNumber = newNumber;
system("pause");
system("cls");
}
void WorkerManager::deleteWorker()
{
//1.判断文件中是否有数据
// 1.1 没有数据 -> 向屏幕输出报错信息(没有数据无法进行删除操作)
// 1.2 有数据 -> 进行步骤2
//2.要求用户输入ID
// 2.1 没有此员工 -> 向屏幕输出报错信息:"没有该员工"
// 2.2 有此员工 -> 在数组中删除此员工,并将数组的情况更新至文件中
if (!this->existInfo())
{
return;
}
int i = 0;
int id = 0;
int index = 0;
cout << "请输入要删除职工的ID:" << endl;
cin >> id;
index = this->isExist(id);
if (index == -1)
{
cout << "没有ID为" << id << "的员工" << endl;
}
else
{
//删除数组中,下标为index的元素
for (i = index; i < this->workerNumber - 1; i++)
{
//该数组中,本质上存放的是Worker类型的一级指针,所以可以直接赋值,而不需要重写等号运算符
this->workerArray[i] = this->workerArray[i + 1];
}
//更新数组中的元素数量
this->workerNumber--;
//将数组中的情况,更新至文件中
this->saveToFile();
//向用户输出提示信息
cout << "删除成功!" << endl;
}
system("pause");
system("cls");
}
void WorkerManager::saveToFile()
{
ofstream ofs;
int i = 0;
ofs.open(FILENAME, ios::out);
for (i = 0; i < this->workerNumber; i++)
{
ofs << this->workerArray[i]->workerId << " "
<< this->workerArray[i]->workerName << " "
<< this->workerArray[i]->departmentName << "\t"
<< this->workerArray[i]->departmentDuty << endl;
}
ofs.close();
}
void WorkerManager::showFromFile()
{
ifstream ifs;
string buf;
//当我们尝试去打开一个文件时,有这几种情况:
// 1.有这个文件 -> 打开成功
// 2.没有这个文件 -> 打开失败
//
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "showFromFile::文件打开失败" << endl;
return;
}
//将文件的内容输出到屏幕上
while (getline(ifs, buf))
{
cout << buf << endl;
}
ifs.close();
system("pause");
system("cls");
}
int WorkerManager::isExist(int id)
{
//我们先不考虑id同名的情况,暂时认为每一个职工ID都是唯一的
//在此前提的基础上,我们查找某一个ID的下标,则只需要遍历一遍数组即可
int i = 0;
for (i = 0; i < this->workerNumber; i++)
{
if (this->workerArray[i]->workerId == id)
{
//找到该员工,直接返回下标
return i;
}
}
//如果程序走到这儿,说明没有该员工。返回 -1
return -1;
}
void WorkerManager::modifyWorkerInfo()
{
//修改员工函数实现思想:
// 1.合法性判断 -> 判断是否有该员工 || 文件里是否有数据 (这2种情况应该输出不同的提示)
// 1.1 没有 -> 提示用户没有该用户,退出函数
// 1.2 有 -> 进行步骤2
// 2.将此员工delete掉
// 3.把新数据存入该空间
if (!this->existInfo())
{
return;
}
bool flag = true;
int idWantModify = 0;
int indexOfIdWantModify = 0;
//员工的新信息
Worker* newInfo = NULL;
int newId = 0;
string newName = "";
int newDepartment = 0;
cout << "请问你要修改的员工的ID是:" << endl;
cin >> idWantModify;
//判断是否存在此员工
indexOfIdWantModify = isExist(idWantModify);
if (indexOfIdWantModify == -1)
{
cout << "该用户不存在" << endl;
system("pause");
system("cls");
return;
}
else
{
delete this->workerArray[indexOfIdWantModify];
cout << "请输入原员工ID为" << idWantModify << "的新ID:" << endl;
cin >> newId;
cout << "请输入新名字:" << endl;
cin >> newName;
cout << "请输入新职务:" << endl;
cout << "1.老板" << endl;
cout << "2.经理" << endl;
cout << "3.普通员工" << endl;
cin >> newDepartment;
while (flag)//在选择处添加一个循环,这样在错误输入时就可以重新输入
{
switch (newDepartment)
{
case BOSS:
newInfo = new Boss(newId, newName);
flag = false;
break;
case MANAGER:
newInfo = new Manager(newId, newName);
flag = false;
break;
case EMPLOYEE:
newInfo = new Employee(newId, newName);
flag = false;
break;
default:
cout << "输入错误!请在职务选择处,输入数字 1~3 的闭区间:" << endl;
break;
}
}
cout << "修改成功!" << endl;
*(this->workerArray + indexOfIdWantModify) = newInfo;//将新数据存入程序数组
this->saveToFile();//更新文件信息
system("pause");
system("cls");
}
}
void WorkerManager::seekWorker()
{
//实现思想:
// 1.合法性判断
// 2.提供两种查找方式
// 2.1 按ID查找(后续需完善程序,使其ID具有唯一性) -> 输出此ID的员工信息
// 2.2 按姓名查找 -> 输出所有拥有该姓名的员工 (考虑到同名情况,我们应该遍历所有员工)
//
if (!this->existInfo())
{
return;
}
int seekBy = 0;
int idWantSeek = 0;
int i = 0;
int indexIdWantSeek = 0;
string nameWantSeek = "";
bool ExistTheWorker = false;
cout << "0.返回上一层" << endl;
cout << "1.按员工ID查找" << endl;
cout << "2.按员工姓名查找" << endl;
while (true)
{
cin >> seekBy;
//合法性判断
if (seekBy != 1 && seekBy != 2 && seekBy != 0)
{
cout << "输入错误!请输入 1, 2 或 0:" << endl;
}
else
{
break;
}
}
switch (seekBy)
{
case RETURN://退出修改函数
cout << "按任意键返回上一菜单" << endl;
break;
case BYID:
{
//1.判断该员工是否存在
// 1.1 存在 -> 输出该员工信息
// 1.2 不存在 -> 询问用户是否继续查找
// 1.2.1 继续 -> 循环switch语句
// 1.2.2 不继续 -> 退出函数,返回主菜单
cout << "请输入要查找的员工ID:" << endl;
cin >> idWantSeek;
indexIdWantSeek = this->isExist(idWantSeek);
if (indexIdWantSeek != -1)
{
//this->workerArray[indexIdWantSeek]->showInfo();
(*(this->workerArray + indexIdWantSeek))->showInfo();
}
else
{
cout << "没有该员工" << endl;
//待实现:询问是否需要重新输入
//.。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
}
break;
case BYNAME:
{
cout << "请输入要查找的员工姓名:" << endl;
cin >> nameWantSeek;
for (i = 0; i < this->workerNumber; i++)
{
if ((*(this->workerArray + i))->workerName == nameWantSeek)
{
(*(this->workerArray + i))->showInfo();
ExistTheWorker = true;
}
}
//遍历结束,没有该员工
if (!ExistTheWorker)
{
cout << "没有姓名为 " << nameWantSeek << " 的员工" << endl;
}
}
break;
default:
break;
}
system("pause");
system("cls");
}
void WorkerManager::sortWorkerById()
{
//实现思想:利用冒泡排序继续
//
if (!this->existInfo())
{
return;
}
int sortStyle = 0;
int i = 0;
int j = 0;
Worker* temp = NULL;
bool swapped = false;
cout << "请选择排序方式:" << endl;
cout << "1.升序" << endl;
cout << "2.降序" << endl;
while (true)
{
//录入用户的选择
cin >> sortStyle;
//合法性判断
if (cin.fail() || sortStyle != 1 && sortStyle != 2)
{
cout << "输入错误,请重新输入 1 或 2 :" << endl;
// 清除错误标志
cin.clear();
// 忽略掉剩余的输入(直到换行符)
cin.ignore(numeric_limits < streamsize >::max(), '\n');
}
else
{
for (i = 0; i < this->workerNumber; i++)
{
for (j = 0; j < this->workerNumber - i - 1; j++)
{
if (sortStyle == 1)
{
//升序 (从小到大)
if ((*(this->workerArray + j))->workerId > (*(this->workerArray + j + 1))->workerId)
{
temp = *(this->workerArray + j);
*(this->workerArray + j) = *(this->workerArray + j + 1);
*(this->workerArray + j + 1) = temp;
swapped = true;
}
}
else
{
//降序 (从大到小)
if ((*(this->workerArray + j))->workerId < (*(this->workerArray + j + 1))->workerId)
{
temp = *(this->workerArray + j);
*(this->workerArray + j) = *(this->workerArray + j + 1);
*(this->workerArray + j + 1) = temp;
swapped = true;
}
}
}
//本次循环没有发生排序行为,即表示当前数据已经有序,提前退出循环
if (!swapped)
{
break;
}
}
//走到这里,说明输入正确,且排序结束
//
// 最后收尾工作:
// 1.输出排序成功提示,
// 2.更新至文档
// 3.跳出循环,结束函数
cout << "排序成功!" << endl;
this->saveToFile();
system("pause");
system("cls");
break;
}
}
}
void WorkerManager::emptyInfo()
{
if (!this->existInfo())
{
return;
}
//实现思想:
// 1. 清空文件信息:删除文件,并创建同名空文件
// 2. 清空程序中数组信息:delete所有workerArrat中的数据
int i = 0;
int doubleConfirmation = 0;
ofstream ofs;
//清空功能比较危险,所以要求用户进行二次确认
cout << "确认清空所有数据吗?" << endl;
cout << "1.确认" << endl;
cout << "2.返回主菜单" << endl;
cin >> doubleConfirmation;
//如果用户输入的是字符或其他任何非 1 的数据,都会直接返回主菜单
if (doubleConfirmation == 1)
{
//清空文件
//这个命令会判断文件是否存在,如果存在,它将删除此文件,并新创建一个同名空文件
//换句话说,它可以实现清空文件内容的功能
ofs.open(FILENAME, ios::trunc);
ofs.close();
//清空数组
if (this->workerArray != NULL)
{
for (i = 0; i < this->workerNumber; i++)
{
if (this->workerArray + i != NULL)
{
delete* (this->workerArray + i);
*(this->workerArray + i) = NULL;
}
}
delete [] this->workerArray;
this->workerArray = NULL;
this->fileEmpty = true;
this->workerNumber = 0;
this->fileWorkerNumber = 0;
}
cout << "清空成功!" << endl;
}
system("pause");
system("cls");
}
void WorkerManager::exitSystem()
{
cout << "已经退出系统,欢迎下次使用" << endl;
exit(0);
}
bool WorkerManager::existInfo()
{
//为什么要设置2个条件?
//因为在这样一种情况时,fileEmpty为false,但实质上却已经没有数据了:打开系统后,利用删除功能,把用户一个个删完
if (this->workerNumber == 0 || this->fileEmpty == true)
{
cout << "文件不存在或文件为空" << endl;
system("pause");
system("cls");
return false;
}
return true;
}
void WorkerManager::showFromArray()
{
// 1.判断数组是否有值
// 1.1 若数组不存在 -> 输出报错信息:“文件不存在或文件为空”
// 1.2 若数组有值 -> 输出数组信息
int i = 0;
if (!this->existInfo())
{
return;
}
else
{
for (i = 0; i < this->workerNumber; i++)
{
this->workerArray[i]->showInfo();
}
}
system("pause");
system("cls");
}
mian.cpp
#include "workerManager.h"
#include "worker.h"
#include "employee.h"
#include "boss.h"
#include "manager.h"
int main()
{
Start();
return 0;
}
写在最后
在每天写bug,debug的过程中,我感受到了无穷的乐趣。
快乐尽在coding中,你也一起爽起来吧!
另外,如果你还有好的项目,欢迎评论或私信,让我也参与进来。