使用C++面向对象程序设计设计一个简易的公司员工信息管理系统,其中用到了:类的使用、数组、循环体、函数的调用、封装等内容。系统需要定义职工(Employee
)类,其中包括工号、姓名、性别、电话、所在科室和工资等信息。实现如下基本功能:
(1)设计菜单实现功能选择;
(2)添加功能:输入人员信息,并保存到文件中;
(3)删除功能:输入人员编号实现删除,并保存到文件中;
(4)修改功能:输入人员编号及其他相关信息,实现修改并保存到文件中;
(5)查询功能:能够根据姓名精确查询人员信息;
(6)统计功能:根据科室分别统计每个科室员工的平均工资并输出结果;
操作系统为Windows 10,工作软件为:Dev-c++ 5.11
系统类型: 64 位操作系统, 基于 x64 的处理器
问题分析:
系统要求实现如下功能:
(1)系统后台
1.原有数据:利用二进制文件存储公司员工信息管理系统的原始数据,存储员工信息。
2.修改信息:在原有数据的基础上,有客户信息需要修改,需要二进制文件的读写操作进行存储和读取数据文件内的信息,并通过链表修改实现文件信息修改。
(2)系统前台
添加加职工信息
删除职工信息
修改职工信息
按姓名查询职工信息
按科室查询职工信息
按科室统计职工平均工资程序模块流程
查看文件中现有职工信息
退出职工信息管理系统
功能结构图
利用循环语句及switch语句实现功能模块多次执行,代码如下:
void show() {
int n;
while (1) {
menu_show();
cin >> n;
switch (n) {
case (0) : {
exit(1); //退出职工工资管理系统
}
case (1): {
AddEmployee();//增加职工信息函数
break;
}
case (2): {
DeleteEmployee();//删除职工信息函数
break;
}
case (3):{
UpdateEmployee();//修改职工信息函数
break;
}
case(4) :{
reseachStudentByName();// 按姓名查询职工信息函数
break;
}
case(5):{
reseachStudentByOffice();//按科室查询职工信息函数
break;
}
case (6) : {
add();//按科室统计职工平均工资函数
break;
}
case(7):{
ap();//显示文件信息函数
break;
}
}
}
}
设计主菜单
员工信息、各科室信息都是由多个基础的数据类型共同组成,因此,使用类进行封装存储。类中的基础数据类型包括浮点型、整形、字符数组。在条件允许的情况下,应多使用new、delete函数以节省空间。
//定义全局变量,每个函数都要使用此链表
Employee *head=NULL,*p1=NULL,*p2=NULL;//head
记录头,p1记录当前,p2开辟
此处的链表记录了员工信息数据,在每个函数里面都要调用,因此定义为全局变量。
源代码中包含了俩个类,一个是Employee
类,用于记录员工的工号、姓名、性别、电话、所在科室和工资,还有一个是office类,用于记录某一科室的名字、人数、成员总工资。
它们实现的功能如下:
Employee
类
数据成员
private:
int id; //记录工号
char name[10]; //记录名字
char sex[5]; //记录性别
char number[12]; //记录电话号码
char office[20]; //科室
double wage; //记录工资
public:
Employee *next; //链表节点
公有成员函数
Employee() {} //无参构造函数
Employee(int id,char na[],char sex[],char num[],char of[],double wa);
//有参构造函数
friend istream &operator>>(istream &cin,Employee &x);//重载运算符">>" 函数
void SaveInfo(); //输出信息函数
char* get_name();//获取名字函数
int get_id();//获取工号函数
char* get_office();//获取科室函数
double get_wage();//获取工资函数
类图
office
类
数据成员
private:
double sum=0;//记录总工资
int count=0;//记录该科室人数
char off[20]; //记录该科室名称
public:
office *next; //链表节点
成员函数
office() //无参构造函数
{strcpy(off," ");}
office(double s,char o[]);//有参构造函数
void display();//输出信息函数
char* get_off(); //获得科室名字函数
void add(double s);//新增该科室成员数据函数
类图
**注:**1.以上各个类中的字符数组均不能换成string类,在存入二进制文件时,string型数据存入的是指针,二进制文件是不能存储指针的
链表每一个节点都是由数据域和指针域构成的,每一个指针域指向下一个节点,每个节点的数据域存储相应的数据,最后一个节点的指针域指向NULL,即空,表示链表结束。访问单链表中任何一个元素都必须通过头结点层层往下遍历,因此头节点是链表中最重要的节点,没有头节点就无法访问整个链表。这也是链表和顺序表最大的不同。
相较于数组,链表则可以动态改变长度,加之指针的方式使得其遍历速度极为高效。
源代码中多处使用到了链表,涉及到链表的初始化、信息修改、增删节点以及遍历链表读取链表信息。源代码中以head指针记录表头,p1指针记录链表尾节点位置,p2指针用于开辟空间,始终保证p1的下一个节点为空指针。
初始化链表及添加节点:
表头head用于记录第一个数据地址,p2开辟空间,然后利用p1将它们链接起来。
当只有一个数据时,3个指针指向同一块地址。实现对员工信息的添加,即在链表的尾节点再接入一个节点,存储新增数据。具体步骤如下,利用循环先从表头开始遍历当前链表,直至尾节点为空指针,p1指针指向当前节点
具体实现代码如下(以添加成员信息功能为例):
if(head)
while(p->next)
{p=p->next;} //获取尾节点 ,经删除函数后,p1会变化,此处重新获取
p1=p;
while(i<=n){
p2=new Employee();
p2->next=NULL;
cout<<"请依次第"<<i<<"个输入工号、名字、性别、电话号码、所在科室 、工资:"<<endl;
cin>>*p2;
if(!head)//头指针为空
{
head=p1=p2;
}
else
{
p1->next=p2;}
p1=p2;
p1->next=NULL;
i++;
cout<<"添加信息成功!"<<endl;
};
修改链表信息:
由于链表存的都是地址,因此,只需要遍历链表找到需要更改的节点处进行修改指针指向内容即可,实现代码如下(以修改员工信息功能为例):
Employee *p=head; //遍历链表代码
while(p)
{
if(strcmp(p->get_name(),na)==0)
{
cout<<"请依次输入修改后的工号、名字、性别、电话号码、所在科室 、工资:"<<endl;
cin>>*(p) ;
cout<<"修改信息成功!"<<endl;
break;
}
p=p->next;
}
删除链表信息:
删除链表信息的方法为,将此节点的下一个节点与上一个节点连接,然后将此节点释放,即可完成信息删除。此处要注意表头信息。
上述过程如下图:
实现代码如下:(以删除员工信息为例)
if(head->get_id()==id)//头链表时
{ cout<<"删除信息成功!"<<endl;
flag=1;
head=head->next;}
while(p->next)
{
if(p->next->get_id()==id)
{
Employee *pp=new Employee();
pp=p->next;
p->next=pp->next;
delete pp;
flag=1;
cout<<"删除信息成功!"<<endl;
break; //删完之后退出
}
p=p->next;
}
该系统中建立了一个二进制文件“员工信息.dat”。引用二进制文件,头文件是不可少的,其次,c++中的利用文件流进行读写文件,因此使用时需要定义文件流对象,每次以二进制方式打开文件并检查是否成功打开。
1.读写文件方法
利用二进制文件读写函数read和write俩大函数,由于次俩函数存储类型为(char*),因此,“员工信息.dat”存储员工的信息,通过将类数据Employee类型进行强制转换为字符数组型读写相应二进制文件。
实现代码如下:
Employee a1(1 ,"李你好", "男", "155", "精神科", 300);
ot.write((char*)&a1,sizeof(Employee));
it.read((char*)&a1,sizeof(Employee));
2.读写文件运用
在此系统中,为了将修改后的信息永久存入文件中,且在内存中修改并永久保存,使用了链表结合二进制文件进行读写。即先将文件信息读入链表,通过链表进行信息增删修改后,再将链表信息以覆盖方式写入文件实现永久储存信息。
上述过程如下图:
首先,如果文件不存在,我们就利用系统自动创建一个“员工信息.dat”文件,如果文件存在,则将原有文件信息读入链表,且确保原有文件数据不被删除
实现代码如下:
ofstream ot;
ot.open("员工信息.dat",ios::out|ios::app);//1.文件不存在时创建文件2.存在时不覆盖原内容
if(!ot)
{
cout<<"员工信息文件打开失败!"<<endl;
exit(1);
}
ot.close();
ifstream it;
it.open("员工信息.dat",ios::in);
if(!it)
{
cout<<"员工信息文件打开失败!"<<endl;
exit(1);
}
it.seekg(0,ios::end);
int filelen,i=1;
filelen=it.tellg();
it.seekg(0,ios::beg);
while(it.tellg()!=filelen)
{
p2=new Employee();
it.read((char*)p2,sizeof(Employee));
if(i==1)
{
head=p1=p2;
}
else
{
p1->next=p2;
}
p1=p2;
p1->next=NULL;
i++;
}
it.close();
在将信息读入链表后,通过对链表修改数据后再存入文件,具体链表修改参照上文。将链表信息写入文件的实现代码如下:
ofstream o;
o.open("员工信息.dat",ios::out);
if(!o)
{cout<<"员工信息文件打开失败!"<<endl;
return;}
Employee *p=head;
while(p)
{
o.write((char *)p,sizeof(Employee));
p=p->next;
}
o.close();
3.读文件信息并输出
将文件的信息读入链表,并遍历链表输出文件里面的信息。
实现代码如下:(以输出文件信息功能为例)
while(it.tellg()!=filelen)
{
p2=new Employee();
it.read((char*)p2,sizeof(Employee));
if(i==1)
{
head=p1=p2;
}
else
{
p1->next=p2;
}
p1=p2;
p1->next=NULL;
i++;
}
it.close();
Employee *p=head; //遍历链表代码
if(head)
while(p)
{
p->SaveInfo();
p=p->next;
}
else cout<<"暂无信息!"<<endl;
主页面
添加职工信息功能
添加3个员工信息如下:
1 张三 男 10010 联通 3050
2 李四 男 10086 移动 4500
3 王五 男 10010 联通 3800
输出此时文件信息
按姓名查询职工信息
以张三名字进行查询
按科室查询职工信息
此处查询联通科室
按科室统计职工平均工资
自动统计并输出所有科室平均工资信息
修改信息
以张三的名字进行修改信息,此处示例仅改变了工资
修改前张三工资为3050,修改后为4000
以张三的工号进行修改信息,此处示例仅改变了工资
张三的工资由4000改至10000
删除信息
以张三名字进行删除张三信息
删除后,文件信息输出中张三的信息不存在了
此处以工号删除王五信息(工号为3)
删除后,文件信息输出时王五的信息不存在
检验文件信息
经过上述操作后,文件中仅剩李四的信息,查看员工信息.dat
文件
通过工号删除文件中李四的信息
再次查询文件中的个人信息
查看员工信息.dat
文件
退出系统
输入数字0,自动退出系统,文件信息自动存储
#include<iostream>
#include<cstring>
#include<fstream>//新增了office类、重载运算符、查看文件信息函数 、根据名字删除信息
#include<iomanip>
using namespace std;
class Employee;
//定义全局变量,每个函数都要使用此链表
Employee *head=NULL,*p1=NULL,*p2=NULL;//head记录头,p1记录当前,p2开辟
class Employee{
private:
int id; //记录工号
char name[10]; //记录名字
char sex[5]; //记录性别
char number[12]; //记录电话号码
char office[20]; //科室
double wage; //记录工资
public:
Employee *next; //链表节点
Employee() //无参构造
{}
Employee(int id,char na[],char sex[],char num[],char of[],double wa);
friend istream &operator>>(istream &cin,Employee &x);//重载运算符">>"
void SaveInfo(); //输出信息
char* get_name();//获取名字
int get_id();//获取工号
char* get_office();//获取科室
double get_wage();//获取工资
};
class office{
private:
double sum=0;//记录总工资
int count=0;//记录该科室人数
char off[20]; //记录该科室名称
public:
office *next;
office() //无参构造初始化
{strcpy(off," ");}
office(double s,char o[]);//有参构造初始化
void display();//输出信息
char* get_off(); //获得科室名字
void add(double s);//新增该科室成员数据
};
Employee::Employee(int id,char na[],char se[],char num[],char of[],double wa) //有参构造函数
{
strcpy(name,na);
strcpy(sex,se);
strcpy(number,num);
strcpy(office,of);
this->id=id;
this->wage=wa;
}
istream &operator>>(istream &in,Employee &x)//重载运算符">>"
{
//cout<<"请依次输入工号、名字、性别、电话号码、所在科室 、工资:"<<endl;
in>>x.id>>x.name>>x.sex>>x.number>>x.office>>x.wage;
return in;
}
void Employee::SaveInfo(){
cout<<"员工姓名:"<<setw(10)<<left<<name<<"性别:"<<setw(10)<<sex<<"工号:"<<setw(5)<<id<<endl;
cout<<"所在科室:"<<setw(10)<<office<<"工资:"<<setw(10)<<wage<<"电话号码:"<<setw(15)<<number<<endl;
}
char* Employee::get_name(){
return name;
}
char* Employee::get_office(){
return office;
}
int Employee::get_id(){
return id;
}
double Employee::get_wage(){
return wage;
}
office::office(double s,char o[]){
sum+=s;
count++;
strcpy(off,o);
}
void office::display(){
cout<<off<<"科室的平均工资为:"<<sum/count<<endl;
}
char* office::get_off(){
return off;
}
void office::add(double s){
sum+=s;
count++;
}
void menu_show(){
cout << "***********************************************************" << endl;
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 << "请输入一个数字选择功能: ";
}
void ad() //将链表数据存入文件
{
ofstream o;
o.open("员工信息.dat",ios::out);
if(!o)
{cout<<"员工信息文件打开失败!"<<endl;
return;}
Employee *p=head;
while(p)
{
o.write((char *)p,sizeof(Employee));
p=p->next;
}
o.close();
}
void ap() //将文件信息读入链表
{
ifstream it;
it.open("员工信息.dat",ios::in);
if(!it)
{
cout<<"员工信息文件打开失败!"<<endl;
exit(1);
}
it.seekg(0,ios::end);
int filelen,i=1;
filelen=it.tellg();
it.seekg(0,ios::beg);
while(it.tellg()!=filelen)
{
p2=new Employee();
it.read((char*)p2,sizeof(Employee));
if(i==1)
{
head=p1=p2;
}
else
{
p1->next=p2;
}
p1=p2;
p1->next=NULL;
i++;
}
//p1->next=NULL; 加上会运行错误
//head->SaveInfo();
it.close();
Employee *p=head; //遍历链表代码
if(head)
while(p)
{
p->SaveInfo();
p=p->next;
}
else cout<<"暂无信息!"<<endl;
}
void AddEmployee() {//1.增加职工信息函数
cout<<"请输入您要添加的职工数量:"<<endl;
int n,i=1;
cin>>n;
Employee *p=head; //遍历链表代码
if(head)
while(p->next)
{p=p->next;} //获取尾节点 ,经删除函数后,p1会变化,此处重新获取
p1=p;
while(i<=n){
p2=new Employee();
p2->next=NULL;
cout<<"请依次第"<<i<<"个输入工号、名字、性别、电话号码、所在科室 、工资:"<<endl;
cin>>*p2;
if(!head)//头指针为空
{
head=p1=p2;
}
else
{
p1->next=p2;}
p1=p2;
p1->next=NULL;
i++;
cout<<"添加信息成功!"<<endl;
};
ad();//刷新文件
// head->next->SaveInfo();
//p1->SaveInfo();
// p2->SaveInfo();
}
void DeleteEmployee() //2.删除员工信息
{
if(!head){
cout<<"没有可供删除的人员信息! 请先添加人员信息!"<<endl;
return;
}
cout<<"输入名字删除输入0,输入工号删除输入1:"<<endl;
int choice;
cin>>choice;
if(choice==1)
{
cout<<"请输入您要删除的员工工号:"<<endl;
int id,flag=0;
cin>>id;
Employee *p=head; //遍历链表代码
if(head->get_id()==id)//头链表时
{ cout<<"删除信息成功!"<<endl;
flag=1;
head=head->next;}
while(p->next)
{
if(p->next->get_id()==id)
{
Employee *pp=new Employee();
pp=p->next;
p->next=pp->next;
delete pp;
flag=1;
cout<<"删除信息成功!"<<endl;
break; //删完之后退出
}
p=p->next;
}
if(flag==0) cout<<"文件中不包含此人信息!"<<endl;
}else if(choice==0){
cout<<"请输入您要删除的员工姓名:"<<endl;
char *na=new char[10];
cin>>na;
int flag=0;
Employee *p=head; //遍历链表代码
if(strcmp(head->get_name(),na)==0)//头链表时
{ cout<<"删除信息成功!"<<endl;
flag=1;
head=head->next;}
while(p->next)
{
if(strcmp(p->next->get_name(),na)==0)
{
Employee *pp=new Employee();
pp=p->next;
p->next=pp->next;
delete pp;
flag=1;
cout<<"删除信息成功!"<<endl;
break; //删完之后退出
}
p=p->next;
}
if(flag==0) cout<<"文件中不包含此人信息!"<<endl;
delete []na;
}
//if(!head->next) head->next=p1; //**当只有表头时,链接起来
ad();//刷新文件
}
void UpdateEmployee()///修改职工信息函数
{
if(!head){
cout<<"没有可供修改的人员信息! 请先添加人员信息!"<<endl;
return;
}
cout<<"输入名字修改输入0,输入工号修改输入1:"<<endl;
int choice;
cin>>choice;
if(choice==0)
{
cout<<"请输入要修改的人名字:"<<endl;
char *na=new char[10];
cin>>na;
Employee *p=head; //遍历链表代码
while(p)
{
if(strcmp(p->get_name(),na)==0)
{
cout<<"请依次输入修改后的工号、名字、性别、电话号码、所在科室 、工资:"<<endl;
cin>>*(p) ;
cout<<"修改信息成功!"<<endl;
break;
}
p=p->next;
}
if(!p) cout<<"未查询到修改人信息!"<<endl;
delete []na;
}
else if(choice==1){
cout<<"请输入要修改的人工号:"<<endl;
int *id=new int;
cin>>*id;
Employee *p=head; //遍历链表代码
while(p)
{
if(p->get_id()==*id)
{
cout<<"请依次输入修改后的工号、名字、性别、电话号码、所在科室 、工资:"<<endl;
cin>>*(p) ;
cout<<"修改信息成功!"<<endl;
break;
}
p=p->next;
}
if(!p) cout<<"未查询到修改人信息!"<<endl;
delete id;
}
//
ad();//刷新文件
}
void reseachStudentByName()//4.按姓名查询职工信息函数
{if(!head){
cout<<"查询失败! 请先添加人员信息!"<<endl;
return;
}
cout<<"请输入要查询的人名字:"<<endl;
char *na=new char[10];
cin>>na;
Employee *p=head; //遍历链表代码
while(p)
{
if(strcmp(p->get_name(),na)==0)
{
cout<<na<<"的个人信息如下:"<<endl;
p->SaveInfo();
break;
}
p=p->next;
}
if(!p) cout<<"未查询到此人信息!"<<endl;
delete []na;
}
void reseachStudentByOffice()//按科室查询职工信息函数
{ if(!head){
cout<<"查询失败! 请先添加人员信息!"<<endl;
return;
}
cout<<"请输入要查询的科室名字:"<<endl;
char *of=new char[10];
cin>>of;
Employee *p=head; //遍历链表代码
int *flag=new int(0);
while(p)
{
if(strcmp(p->get_office(),of)==0)
{if(*flag==0) cout<<"所查询的科室职工如下:"<<endl;
p->SaveInfo();
(*flag)++;
}
p=p->next;
}
if(!(*flag)) cout<<"未查询到该科室人员信息!"<<endl;
delete []of;
delete flag;
}
void add()//按科室统计职工平均工资函数
{
if(!head){
cout<<"统计失败! 请先添加人员信息!"<<endl;
return;
}
office *head_=NULL,*p1_=NULL,*p2_=NULL;//前三者同之前
Employee *p=head; //遍历链表代码
office *p_;
int i=1;
while(p)
{
int f=0;//作为标记
for( p_=head_;p_;p_=p_->next) //遍历链表head_
{if(strcmp(p_->get_off(),p->get_office())==0)
{
p_->add(p->get_wage()); //添加
f=1;
break;
}
}
if(f==0) //新增,存入链表head_
{
p2_=new office(p->get_wage(),p->get_office());
if(i==1)
{
head_=p1_=p2_;
}
else
{
p1_->next=p2_;
}
p1_=p2_;
p1_->next=NULL;
i++;
}
p=p->next;
}
p_=head_; //遍历链表代码
while(p_)
{
p_->display();
p_=p_->next;
}
}
void show() {
int n;
while (1) {
menu_show();
cin >> n;
switch (n) {
case (0) : {
exit(1); //退出职工工资管理系统
}
case (1): {
AddEmployee();//增加职工信息函数
break;
}
case (2): {
DeleteEmployee();//删除职工信息函数
break;
}
case (3):{
UpdateEmployee();//修改职工信息函数
break;
}
case(4) :{
reseachStudentByName();// 按姓名查询职工信息函数
break;
}
case(5):{
reseachStudentByOffice();//按科室查询职工信息函数
break;
}
case (6) : {
add();//按科室统计职工平均工资函数
break;
}
case(7):{
ap();//显示文件信息函数
break;
}
}
}
}
int main(){
//getdate();
ofstream ot;
ot.open("员工信息.dat",ios::out|ios::app);//1.文件不存在时创建文件2.存在时不覆盖原内容
if(!ot)
{
cout<<"员工信息文件打开失败!"<<endl;
exit(1);
}
//ot.write((char*)&a1,sizeof(Employee));
ot.close();
ifstream it;
it.open("员工信息.dat",ios::in);
if(!it)
{
cout<<"员工信息文件打开失败!"<<endl;
exit(1);
}
it.seekg(0,ios::end);
int filelen,i=1;
filelen=it.tellg();
it.seekg(0,ios::beg);
while(it.tellg()!=filelen)
{
p2=new Employee();
it.read((char*)p2,sizeof(Employee));
if(i==1)
{
head=p1=p2;
}
else
{
p1->next=p2;
}
p1=p2;
p1->next=NULL;
i++;
}
//head->SaveInfo();
it.close();
show();
}