职工系统
增删改查之职工系统
自己写的职工系统
0. 功能介绍
该系统拥有的功能如下,具体还是增删改查,但是会将输入的人员信息保存为一个txt文件。
具体而言是,有三个部门:普通员工,经理和老板。每个部门的员工都具有姓名,编号和对应的岗位职责。我们需要分类存储所有的员工。
1. 类的声明和各自的功能
1.1 管理类
管理类的目的是统一所有函数的接口,将所有需要实现的功能统一写成这个管理类的成员函数,这样就可以在主程序中 通过实例化这个类来调用所有的功能 ,形式上统一且方便。
从管理类上也可以看出书写整个程序的思路。
#ifndef WORKMANAGER_H
#define WORKMANAGER_H
#include <iostream>
#include <string>
#include <fstream>
#include "Employee.h"
#include "Manager.h"
#include "Boss.h"
#define FileName "WorkManager.txt"
//管理整个系统的类:管理类,所有操作都写在这个类里面
class WorkManager
{
public:
//获取文件中员工数量
void getNuminFile();
//用于从文件中读信息存入内存,在显删修查等功能中需要用到的函数
void FiletoMemory();
//管理类的构造函数,初始化一些变量
WorkManager();
//用于显示菜单界面的函数
void MenuDisplay();
//用于退出系统的函数
bool SystemExit();
//用于添加职工的函数
void StuffAdd();
//用于把新添加的员工写入文件中的函数
void MemorytoFile(const int &newadd);
//用于把All_StuffArray中数据写入文件的函数
void AlloFile();
//用于显示文件中所有员工
void StuffShow();
//用于删除指定姓名与编号的员工的函数
void StuffDelete();
//用于修改指定姓名与编号的员工信息的函数
void StuffChange();
//用于查找指定姓名或者编号的员工
void StuffFind();
//用于按照编号排序的函数
void BubbleSort_id();
//用于清空文件的函数
void StuffClear();
//析构函数,释放一些内存
~WorkManager();
public:
//记录现在内存中的员工个数
int m_NuminMemory;
//记录文件中员工个数
int m_NuminFile;
//仅用于管理新添加的员工
AbstractStuff **Newadd_StuffArray;
//在读文件时管理所有员工
AbstractStuff **All_StuffArray;
//判断最新的文件内容是否经过函数读入内存中
bool Flag_FiletoMemory;
//判断文件是否存在或者文件内容是否为空
bool Flag_Empty;
};
#endif
1.2 抽象员工类
由于每个员工都具有三种相同的成员:姓名,编号和所属部门,因此非常合适设计一个抽象员工类,包含这三个成员,再让各自部门的员工继承这个抽象类,减少代码量。
作为抽象类,需要至少一个纯虚函数。在这里,我选择把展示每个员工职责的部分写作纯虚函数,可以利用多态来使用这个函数。
作为抽象类,它只有声明,没有函数实现,因此只有头文件,没有源文件。
#ifndef ABSTRACTSTUFF_H
#define ABSTRACTSTUFF_H
#include <iostream>
#include <string>
//用于实现多态的抽象员工类,成员为所有员工的姓名,编号与部门,还有职责
class AbstractStuff
{
public:
//员工职责
virtual void StuffDuty() = 0;
public:
//员工姓名
std::string m_name;
//员工编号
std::string m_id;
//员工部门
std::string m_depart;
};
#endif
1.3 员工类
员工类是以部门的不同进行分类设计的。
以Employee为例:
声明
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <iostream>
#include <string>
#include "AbstractStuff.h"
//继承抽象员工类的普通职工类,需要重写职责函数
class Employee : public AbstractStuff
{
public:
//普通员工职责
virtual void StuffDuty();
//普通员工构造函数,初始化姓名
Employee(const std::string &name, const std::string &id, const std::string &depart);
};
#endif
实现
#include "Employee.h"
using namespace std;
//普通员工职责
void Employee::StuffDuty()
{
cout << "干活" << endl;
}
//普通员工构造函数,初始化姓名
Employee::Employee(const std::string &name,const std::string &id,const std::string &depart)
{
this->m_name = name;
this->m_id = id;
this->m_depart = depart;
}
我在这个类里就实现了两个函数,一个是自身的构造函数,用于初始化继承过来的三个成员变量;另一个就是重写的纯虚函数,用于向用户输出该员工的职责。
剩下的两个员工类分别为Boss和Manager,除了纯虚函数外均一样。
2. 菜单功能
菜单功能用于向用户展示可以使用哪些功能。
//用于显示菜单界面的函数
void WorkManager::MenuDisplay()
{
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;
}
这个功能比较简单,无需多言。
3. 退出功能
作用是退出这个系统,有一个布尔类型的返回值。首先会询问是否确定退出,待用户选择退出后就返回一个真值;倘若用户决定不退出了,就返回一个假值;若用户输入了其他非法选项,就提示输入错误,然后同样返回假值。
//用于退出系统的函数
bool WorkManager::SystemExit()
{
cout << "确定退出?" << endl;
cout << "Y:确定\t" << "N:返回" << endl;
string flag;
cin >> flag;
//选择退出则返回true
if ( flag == "Y" || flag == "y")
{
cout << "欢迎下次使用" << endl;
return true;
}
//选择返回则返回false
else if (flag == "N" || flag == "n")
{
cout << "欢迎回来" << endl;
system("pause");
system("cls");
return false;
}
else
{
cout << "输入错误" << endl;
system("pause");
system("cls");
return false;
}
}
在主函数中的调用如下:
case '0':
if (Wm.SystemExit()) return 0;
break;
在这里省略了Wm的实例化与switch的部分格式。代码表达的是,如果是case ‘0’ 的情况,就调用Wm的退出系统函数,如果用户确定退出,就返回true,然后return 0,结束主函数;如果用户不退出或者输入非法,就返回false,然后直接跳出switch,不执行任何程序。
4. 增添功能
首先询问用户准备新添加几位员工,通过输入流对象获取用户的输入。这里使用string类型接收是为了防止用户的非法输入:如果使用int类型的话,当用户输入字符时程序会卡死。
随后使用 atoi() 函数将string类型的数转为int类型。
随后判断用户的输入是否为正数,不是的话输出“输入有误,请重新选择”的字样提醒用户,然后退出程序。若用户的输入无误,进行下一步。
考虑到用户每完成一位人员的输入都需要实例化一个对应的对象,而且需要考虑到多态的实现,这个对象应当new在堆区,用一个抽象类指针来接收它。
但是用户会批量输入一群员工的信息,这要求我们利用一个数组来接收这群人员的信息。
同时,我们不能每输入一次就去弄一个数组,我们希望在最后这些人员应该放在同一个数组 下。换言之,这个数组的长度必须是动态可调的。
为了实现这个功能,必须利用抽象类的二级指针来控制这个数组。
因此,在管理类下创建两个成员:一个时抽象员工类的二级指针,另一个负责统计在程序不关闭的情况下,用户一次次地用添加函数输入了多少员工。这就是Newadd_StuffArray和m_NuminMemory。
当用户输入无误后,计算需要新准备多长的数组,其值应当等于当前打开系统后输入的所有员工数量;然后准备一个这个长度的抽象类的二级指针。
然后我们判断是否第一次操作添加函数,否的话应当将曾经二级指针的内容全部转移至新建立的NewArray_Stuff 中去。
随后我们通过抽象类的一级指针new不同的子类并添加到NewArray_Stuff 中。然后修改和置位一些标志和统计作用的成员。
最后我们需要将内存中的东西写入一个txt文件中,因此设计了一个利用追加的方式写文件的函数。
//用于添加职工的函数
void WorkManager::StuffAdd()
{
cout << "您想新添加几位新员工?" << endl;
string key; //记录准备添加几名员工
cin >> key;
int newadd = atoi(key.c_str());
if (newadd > 0)
{
//计算应当准备多长的数组,应当等于之前存在内存中的数量+新添加的数量
int length = this->m_NuminMemory + newadd;
//准备一个新的数组用于存储之前存入内存的数据以及现在即将存入的数据
AbstractStuff **NewArray_Stuff = new AbstractStuff *[length];
//如果Newadd_StuffArray指向非空,即代表有操作过添加函数
if (!Newadd_StuffArray == NULL)
{
//这个时候就把原内存内的放到这个新的NewArray_Stuff中
for (int i = 0; i != this->m_NuminMemory; ++i)
{
NewArray_Stuff[i] = this->Newadd_StuffArray[i];
}
}
//用户输入新员工,并存入内存中
string name;
string id;
char depart;
AbstractStuff *Stuff = NULL;
for (int i = 0; i != newadd; ++i)
{
cout << "请输入第 " << i + 1 << "位新员工的姓名:" << endl;
cin >> name;
cout << "请输入第 " << i + 1 << "位新员工的编号:" << endl;
cin >> id;
cout << "请输入第 " << i + 1 << "位新员工的部门:" << endl;
cout << "A:普通员工\t" << "B:经理\t" << "C:老板" << endl;
cin >> depart;
switch (depart)
{
case 'A':
case 'a':Stuff = new Employee(name, id, "普通员工"); break;
case 'B':
case 'b':Stuff = new Manager(name, id, "经理"); break;
case 'C':
case 'c':Stuff = new Boss(name, id, "老板"); break;
}
NewArray_Stuff[i + this->m_NuminMemory] = Stuff;
}
//把记录内存中员工数量的变量增加
this->m_NuminMemory += newadd;
//清掉以前的二级指针,换成现在的二级指针
delete[] this->Newadd_StuffArray;
this->Newadd_StuffArray = NewArray_Stuff;
//把内存中的数据存入文件
this->MemorytoFile(newadd);
//统计文件中的员工数量
this->m_NuminFile += newadd;
//新添加了员工,但是没有更新All_StuffArray,应该把FiletoMemory()的标志置为false
this->Flag_FiletoMemory = false;
//这是写了数据在文件中了,应该把标志位取为false
this->Flag_Empty = false;
}
else
{
cout << "输入有误,请重新选择" << endl;
}
system("pause");
system("cls");
}
利用追加的方式写文件的函数:
//用于把新添加的员工写入文件中的函数
void WorkManager::MemorytoFile(const int &newadd)
{
//计划利用追加的方式写文件,因此实际上只要写入新添加的员工即可
ofstream file(FileName,ios::app);
for (int i = 0; i != newadd; ++i)
{
file << this->Newadd_StuffArray[this->m_NuminMemory - newadd + i]->m_name << "\t"
<< this->Newadd_StuffArray[this->m_NuminMemory - newadd + i]->m_id << "\t"
<< this->Newadd_StuffArray[this->m_NuminMemory - newadd + i]->m_depart
<< endl;
}
file.close();
}
5.显示功能
显示功能要求显示所有员工的姓名、编号、部门与职责。
我们首先读取文件内容,并试图将其显示在cmd上。由于我们需要显示每一位职工的职责,而这是利用多态实现的,因此我们不能简单读取所有文本就显示,必须对每一行的文本进行判断,并new出对应的类对象,再将其统一地利用抽象类的二级指针进行管理。
//用于显示文件中所有员工
void WorkManager::StuffShow()
{
if (!this->Flag_FiletoMemory)
{
this->FiletoMemory();
}
if (this->Flag_Empty)
{
cout << "数据为空" << endl;
system("pause");
system("cls");
return;
}
for (int i = 0; i != this->m_NuminFile; ++i)
{
cout << "姓名: " << this->All_StuffArray[i]->m_name << "\t"
<< "编号: " << this->All_StuffArray[i]->m_id << "\t"
<< "部门: " << this->All_StuffArray[i]->m_depart << "\t"
<< "\t职责: ";
this->All_StuffArray[i]->StuffDuty();
}
system("pause");
system("cls");
}
上面展示的是显示函数的具体实现,其中第一个判断是为了确认是否曾经将文件内的数据读入内存了,Flag_FiletoMemory是FiletoMemory()的标志,如果为false,即代表未曾进行过读的操作。
FiletoMemory()函数的实现如下:
//用于从文件中读信息存入内存,在显删修查等功能中需要用到的函数
void WorkManager::FiletoMemory()
{
ifstream file(FileName,ios::in);
//判断文件是否存在,即是否第一次使用该系统
if (!file.is_open())
{
return;
}
string name;
string id;
string depart;
AbstractStuff **StuffArray = new AbstractStuff *[this->m_NuminFile];
for (int i = 0; i != this->m_NuminFile; ++i)
{
AbstractStuff *Stuff = NULL;
file >> name >> id >> depart;
if (depart == "普通员工")
{
Stuff = new Employee(name, id, depart);
}
if (depart == "经理")
{
Stuff = new Manager(name, id, depart);
}
if (depart == "老板")
{
Stuff = new Boss(name, id, depart);
}
StuffArray[i] = Stuff;
}
if (!this->All_StuffArray == NULL)
{
delete[] this->All_StuffArray;
}
this->All_StuffArray = StuffArray;
//执行过该函数了,因该置为true,防止重复执行
this->Flag_FiletoMemory = true;
}
6. 删除功能
删除功能要求用户输入员工的编号与姓名。系统在内存中遍历所有类对象,当找到姓名与编号匹配的员工的时候就删除该员工。删除的办法是使用二级指针利用后面的元素去覆盖前面的元素。
同样需要首先判断是否运行过FiletoMemory()函数,否则统计值可能会出问题。
这里存在一个问题,即当检索的员工是文本文件中最后一个员工的时候,由于利用后一个元素覆盖前一个元素,因此下标会溢出。
所以先判断一下要删除的是不是最后一个员工,是的话就把这个指针置空。
//用于删除指定姓名或者编号的员工
void WorkManager::StuffDelete()
{
//如果没有运行过FiletoMemory(),运行一下
if (!this->Flag_FiletoMemory)
{
this->FiletoMemory();
}
if (this->Flag_Empty)
{
cout << "数据为空" << endl;
system("pause");
system("cls");
return;
}
cout << "输入您想删除的员工姓名:" << endl;
string name;
cin >> name;
cout << "输入您想删除的员工编号:" << endl;
string id;
cin >> id;
bool Aflag = false;
for (int i = 0; i != this->m_NuminFile; ++i)
{
if (this->All_StuffArray[i]->m_name == name && this->All_StuffArray[i]->m_id == id)
{
cout << "姓名: " << this->All_StuffArray[i]->m_name
<< "\t编号: " << this->All_StuffArray[i]->m_id << endl;
cout << "A:确定删除\t" << "B:放弃删除" << endl;
string flag;
cin >> flag;
if (flag == "A" || flag == "a")
{
if (i != m_NuminFile - 1)
{
for (int j = i; j < this->m_NuminFile - 1; ++j)
{
this->All_StuffArray[j] = this->All_StuffArray[j + 1];
}
}
else
{
this->All_StuffArray[i] = NULL;
}
--this->m_NuminFile;
Aflag = true;
int a = 0;
cout << a << endl;
cout << "员工: " << name << "\t编号:" << id << "\t已删除" << endl;
//将All_StuffArray写入文件
this->AlloFile();
system("pause");
system("cls");
return;
}
else if (flag == "b" || flag == "B")
{
cout << "已取消" << endl;
system("pause");
system("cls");
return;
}
else
{
cout << "输入错误" << endl;
system("pause");
system("cls");
return;
}
}
}
if (!Aflag)
{
cout << "没有与之匹配的员工姓名与编号" << endl;
system("pause");
system("cls");
return;
}
}
7. 修改功能
与删除功能类似,前置的函数已经基本完成了。
//用于修改员工信息的函数
void WorkManager::StuffChange()
{
//如果没有运行过FiletoMemory(),运行一下
if (!this->Flag_FiletoMemory)
{
this->FiletoMemory();
}
if (this->Flag_Empty)
{
cout << "数据为空" << endl;
system("pause");
system("cls");
return;
}
cout << "输入您想修改的员工姓名:" << endl;
string name;
cin >> name;
cout << "输入您想修改的员工编号:" << endl;
string id;
cin >> id;
bool flagchange = false;
for (int i = 0; i != this->m_NuminFile; ++i)
{
if (this->All_StuffArray[i]->m_name == name && this->All_StuffArray[i]->m_id == id)
{
cout << "输入修改后的名字:" << endl;
cin >> this->All_StuffArray[i]->m_name;
cout << "输入修改后的编号:" << endl;
cin >> this->All_StuffArray[i]->m_id;
cout << "输入修改后的部门:" << endl;
cout << "A:普通员工\t" << "B:经理\t" << "C:老板\t" << endl;
char flag;
cin >> flag;
switch (flag)
{
case 'A':
case 'a':this->All_StuffArray[i]->m_depart = "普通员工"; break;
case 'B':
case 'b':this->All_StuffArray[i]->m_depart = "经理"; break;
case 'C':
case 'c':this->All_StuffArray[i]->m_depart = "老板"; break;
default:break;
}
flagchange = true;
cout << "员工: " << name << "\t编号:" << id << "\t已修改为" << endl;
cout << "员工: " << this->All_StuffArray[i]->m_name
<< "\t编号:" << this->All_StuffArray[i]->m_id
<< "\t部门:" << this->All_StuffArray[i]->m_depart << endl;
}
}
if (!flagchange)
{
cout << "没有与之匹配的员工姓名与编号" << endl;
system("pause");
system("cls");
return;
}
//将All_StuffArray写入文件
this->AlloFile();
system("pause");
system("cls");
}
8. 查找功能
//用于查找指定姓名或者编号的员工
void WorkManager::StuffFind()
{
//如果没有运行过FiletoMemory(),运行一下
if (!this->Flag_FiletoMemory)
{
this->FiletoMemory();
}
if (this->Flag_Empty)
{
cout << "数据为空" << endl;
system("pause");
system("cls");
return;
}
cout << "请输入想寻找的员工姓名或编号:" << endl;
string key;
cin >> key;
bool flag = false;
for (int i = 0; i != this->m_NuminFile; ++i)
{
if (this->All_StuffArray[i]->m_name == key || this->All_StuffArray[i]->m_id == key)
{
flag = true;
cout << "姓名:" << this->All_StuffArray[i]->m_name
<< "\t编号:" << this->All_StuffArray[i]->m_id
<< "\t部门:" << this->All_StuffArray[i]->m_depart
<< "\t职责:";
this->All_StuffArray[i]->StuffDuty();
}
}
if (!flag)
{
cout << "输入有误或查无此人" << endl;
}
system("pause");
system("cls");
}
9.排序功能
//用于按照编号排序的函数
void WorkManager::BubbleSort_id()
{
//如果没有运行过FiletoMemory(),运行一下
if (!this->Flag_FiletoMemory)
{
this->FiletoMemory();
}
if (this->Flag_Empty)
{
return;
}
cout << "请选择顺序或逆序排序" << endl;
cout << "A:顺序" << "\tB:逆序" << endl;
string flag;
cin >> flag;
if (flag == "A" || flag == "a")
{
for (int i = 0; i != this->m_NuminFile - 1; ++i)
{
for (int j = 0; j != this->m_NuminFile - i - 1; ++j)
{
AbstractStuff *temp = NULL;
if (atoi(this->All_StuffArray[j]->m_id.c_str()) >= atoi(this->All_StuffArray[j + 1]->m_id.c_str()))
{
temp = this->All_StuffArray[j];
this->All_StuffArray[j] = this->All_StuffArray[j + 1];
this->All_StuffArray[j + 1] = temp;
}
}
}
}
else if (flag == "b" || flag == "B")
{
for (int i = 0; i != this->m_NuminFile - 1; ++i)
{
for (int j = 0; j != this->m_NuminFile - i - 1; ++j)
{
AbstractStuff *temp = NULL;
if (atoi(this->All_StuffArray[j]->m_id.c_str()) < atoi(this->All_StuffArray[j + 1]->m_id.c_str()))
{
temp = this->All_StuffArray[j];
this->All_StuffArray[j] = this->All_StuffArray[j + 1];
this->All_StuffArray[j + 1] = temp;
}
}
}
}
else
{
cout << "无效输入" << endl;
return;
}
//将All_StuffArray写入文件
this->AlloFile();
}
10. 清空功能
//用于清空文件的函数
void WorkManager::StuffClear()
{
cout << "确定清空所有员工信息吗?" << endl;
cout << "A:确定" << "\tB:取消" << endl;
string flag;
cin >> flag;
if (flag == "A" || flag == "a")
{
this->m_NuminFile = 0;
this->m_NuminMemory = 0;
if (!this->All_StuffArray == NULL || !this->Newadd_StuffArray == NULL)
{
delete[] this->All_StuffArray;
this->All_StuffArray = NULL;
delete[] this->Newadd_StuffArray;
this->Newadd_StuffArray = NULL;
}
this->Flag_Empty = true;
ofstream file(FileName, ios::out);
//file << endl;
file.close();
cout << "已清空" << endl;
system("pause");
system("cls");
return;
}
else if (flag == "b" || flag == "B")
{
cout << "已取消" << endl;
system("pause");
system("cls");
return;
}
else
{
cout << "无效输入" << endl;
system("pause");
system("cls");
return;
}
}
11. 主函数
#include "WorkManager.h"
using namespace std;
int main()
{
WorkManager Wm;
while (true)
{
Wm.MenuDisplay();
char flag;
cin >> flag;
switch (flag)
{
case '0':
if (Wm.SystemExit()) return 0;
break;
case '1':
Wm.StuffAdd();
break;
case '2':
Wm.StuffShow();
break;
case '3':
Wm.StuffDelete();
break;
case '4':
Wm.StuffChange();
break;
case '5':
Wm.StuffFind();
break;
case '6':
Wm.BubbleSort_id();
Wm.StuffShow();
break;
case '7':
Wm.StuffClear();
break;
default:break;
}
}
}
总结
对于这个系统的编写,最难的是类的设计。最容易想到的是抽象员工类与员工子类的设计以及各自方法的定义。倒是从来没有想过去设计一个管理类来管理所有的函数,相当于提供一个接口把 。事实上把这些函数写成全局函数完全是ok的,但是写成管理类就很清晰明了了。这是一点。
其二就是抽象类的二级指针,完全没想过可以利用抽象类的二级指针去动态地管理一个数组,这是一个方法,虽然我觉得这个方法非常浪费内存,相比以后会有类似的但是节省内存的方法。
以上就是这次编写最重要的两点了吧,应该。