【黑马程序员】员工管理系统项目实战

10 篇文章 0 订阅
3 篇文章 0 订阅

20240221

需求分析

  • 本教程主要利用C++来实现一个基于多态的职工管理系统

员工信息

构成

  • 普通员工
  • 经理
  • 老板

员工显示

  • 需要显示职工编号、职工姓名、职工岗位以及职责

不同员工职责

  • 责普通员工职责:完成经理交给的任务
  • 经理职责:完成老板交给的任务,并下发任务给员工
  • 老板职责:管理公司所有事务

主要功能

  • 退出管理程序:退出当前管理系统
  • 增加职工信息:实现批量添加职工功能,将信息录入到文件中,职工信息为:职工编号、姓名、部门编号
  • 显示职工信息:显示公司内部所有职工的信息
  • 删除离职职工:按照编号删除指定的职工
  • 修改职工信息: 按照骗号修改职工个人信息
  • 查找职工信息:按照职工的编号或者职工的姓名进行查找相关的人员信息
  • 按照编号排序:按照职工编号,进行排序,排序规则由用户指定
  • 清空所有文档:清空文件中记录的所有职工信息 (清空前需要再次确认,防止误删)

功能实现

创建管理类

创建管理类头文件

  • 新建workerManage.hpp头文件
  • 完成功能
    • 包重复引用检查
    • 引入对应头文件和响应的命名空间
    • 声明对应的构造和析构函数
  • 代码示例
#pragma once

#include <iostream>
using namespace std;

// 员工管理类
class WorkerManager {
public:
    // 构造函数
    WorkerManager();

    // 析构函数
    ~WorkerManager();
};

创建管理类源文件

  • 新建workerManage.cpp源文件

  • 完成功能

    • 引入workerManage.hpp头文件

    • 实现两个空的析构和构造函数

  • 代码示例

#include "workerManage.hpp"

WorkerManager::WorkerManager() {

}

WorkerManager::~WorkerManager() {

}

菜单功能实现

声明菜单功能

  • workerManage.hpp头文件类中添加菜单声明函数

  • 代码示例

    // 展示菜单
    void ShowMenu();

实现菜单功能

  • workerManage.cpp源文件中实现菜单函数

  • 代码示例

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 << "*********    5.查找职工信息    *********" << endl;
    cout << "*********    6.查找职工信息    *********" << endl;
    cout << "*********    7.清空所有文档    *********" << endl;
    cout << "****************************************" << endl;
    cout << endl;
}

编写菜单功能测试

  • 新建main.cpp文件

  • 代码示例

#include "workerManage.hpp"

int main() {
    WorkerManager wm;
    wm.ShowMenu();
    return 0;
}

测试结果

在这里插入图片描述

退出功能

  • 编写功能框架,并在用户输入0时结束
int main() {
    WorkerManager wm;
    int choice;
    while (true) {
        wm.ShowMenu();
        cout << "请输入你的选择:";
        cin >> choice;
        switch (choice) {
            case 0: // 退出系统
                exit(0);
                break;
            case 1: // 增加职工
                break;
            case 2: // 显示职工信息
                break;
            case 3: // 删除离职职工
                break;
            case 4: //修改职工信息
                break;
            case 5: //查找职工信息
                break;
            case 6: //按照编号排序
                break;
            case 7: //清空所有文档
                break;
            default: // 清屏
                break;
        }
    }
    return 0;
}

增加功能

功能分析

  • 根据需求,员工有三类,且每个员工有共性也有区别,因此考虑用多态的方式

  • 将共性的成员抽象在基类中,在不同员工实现类中进行不同特性的实现

  • 首先给出员工的抽象类worker.hpp

  • 代码示例

#pragma once

#include <iostream>
#include <string>

using namespace std;

// 员工抽象类
class Worker {
public:
    // 展示员工信息
    virtual void ShowWorkerInfo() = 0;

    // 获取部门名
    virtual string getDeptName(int deptId) = 0;

    // 获取岗位职责
    virtual string getWorkerAbility() = 0;

    // 员工编号
    int m_id;
    // 姓名
    string m_name;

    // 部门id
    int m_deptId;
};

普通员工类实现

  • 普通员工继承自抽象的worker类

  • 首先在employee.hpp中声明抽象类中的方法

#include "worker.hpp"

// 普通员工类
class Employee : public Worker {
public:
    // 构造函数
    Employee(int id, string name, int deptId);

    // 展示员工信息
    virtual void ShowWorkerInfo();

    // 获取部门名
    virtual string getDeptName(int deptId);

    // 获取岗位职责
    virtual string getWorkerAbility();
};
  • employee.cpp中实现头文件中声明的方法
#include "employee.hpp"

// 构造函数
Employee::Employee(int id, string name, int deptId) {
    this->m_id = id;
    this->m_name = name;
    this->m_deptId = deptId;
}

// 展示员工信息
void Employee::ShowWorkerInfo() {
    cout << "员工编号:" << this->m_id
         << "\t员工姓名:" << this->m_name
         << "\t员工部门:" << this->getDeptName()
         << "职责:" << this->getWorkerAbility() << endl;
}

// 获取部门名
string Employee::getDeptName(int deptId) {
    return "普通员工";
}

// 获取岗位职责
string Employee::getWorkerAbility() {
    return "完成经理交给的任务";
}
  • 普通员工实现测试
#include "worker.hpp"
#include "employee.hpp"

int main() {
    Worker* worker = Worker* worker = new Employee(1,"zs", 3);
    worker->ShowWorkerInfo();
    return 0;
}
  • 测试结果

在这里插入图片描述

经理类实现

  • 按照员工的思路实现经理类

  • 经理类头文件manager.hpp

#include "worker.hpp"

// 普通员工类
class Manager : public Worker {
public:
    // 构造函数
    Manager(int id, string name, int deptId);

    // 展示员工信息
    virtual void ShowWorkerInfo();

    // 获取部门名
    virtual string getDeptName(int deptId);

    // 获取岗位职责
    virtual string getWorkerAbility();
};
  • 经理类具体实现manager.cpp
#include "manager.hpp"

// 构造函数
Manager::Manager(int id, string name, int deptId) {
    this->m_id = id;
    this->m_name = name;
    this->m_deptId = deptId;
}

// 展示员工信息
void Manager::ShowWorkerInfo() {
    cout << "员工编号:" << this->m_id
         << "\t员工姓名:" << this->m_name
         << "\t员工部门:" << this->getDeptName()
         << "\t职责:" << this->getWorkerAbility() << endl;
}

// 获取部门名
string Manager::getDeptName(int deptId) {
    return "经理";
}

// 获取岗位职责
string Manager::getWorkerAbility() {
    return "完成老版交给的任务,并下发任务给普通员工";
}

老板类实现

  • 按照员工的思路实现老板类

  • 老板类头文件boss.hpp

#include "worker.hpp"

// 普通员工类
class Boss : public Worker {
public:
    // 构造函数
    Boss(int id, string name, int deptId);

    // 展示员工信息
    virtual void ShowWorkerInfo();

    // 获取部门名
    virtual string getDeptName(int deptId);

    // 获取岗位职责
    virtual string getWorkerAbility();
};
  • 老板类具体实现boss.cpp
#include "boss.hpp"

// 构造函数
Boss::Boss(int id, string name, int deptId) {
    this->m_id = id;
    this->m_name = name;
    this->m_deptId = deptId;
}

// 展示员工信息
void Boss::ShowWorkerInfo() {
    cout << "员工编号:" << this->m_id
         << "\t员工姓名:" << this->m_name
         << "\t员工部门:" << this->getDeptName()
         << "\t职责:" << this->getWorkerAbility() << endl;
}

// 获取部门名
string Boss::getDeptName(int deptId) {
    return "老板";
}

// 获取岗位职责
string Boss::getWorkerAbility() {
    return "下发任务给经理";
}

测试多态

#include "worker.hpp"
#include "employee.hpp"
#include "manager.hpp"
#include "boss.hpp"

// TODO:delete 删除worker的时候在MAC下存在问题,待排查
int main() {
    Worker *worker = NULL;
    worker = new Employee(1, "zs", 1);
    worker->ShowWorkerInfo();

    worker = new Manager(2, "ls", 2);
    worker->ShowWorkerInfo();

    worker = new Boss(3, "ww", 3);
    worker->ShowWorkerInfo();
    return 0;
}

添加职工

  • 批量添加职工,并且保存到文件中

功能分析

  • 职工在创建的时候有不同的工种,如果想要将不同种类的员工都放在一个数组中,可以将所有员工的指针都维护到一个数组中

  • 如果想要维护不定长的数组,可以将数组开辟到堆区,使用worker**的指针维护

功能实现

  • 在员工管理头文件workerManage.hpp中添加员工数量和维护员工指针,并引入相应的头文件
#include "worker.hpp"
#include "employee.hpp"
#include "manager.hpp"
#include "boss.hpp"

    // 批量添加职工
    void AddEmp();

private:
    // 员工数量
    int memberSize;
    // 员工数组指针
    Worker **pWorker;
  • 在员工管理源文件workerManage.cpp中实现构造函数、析构函数和批量添加员工逻辑
WorkerManager::WorkerManager() {
    this->memberSize = 0;
    this->pWorker = NULL;
}

WorkerManager::~WorkerManager() {
    if (this->pWorker != NULL) {
        delete[] this->pWorker;
        this->pWorker = NULL;
    }
}

void WorkerManager::AddEmp() {
    // 要添加员工的数量
    int addNum;
    cout << "请输入要添加员工的数量: " << endl;
    cin >> addNum;
    if (addNum > 0) {
        // 记录添加后的人数=当前已有人数+新增人数
        int curSize = this->memberSize + addNum;
        Worker **newArr = new Worker *[curSize];
        // 如果之前已经存在职工,现将已有职工进行拷贝
        if (this->memberSize > 0) {
            for (int i = 0; i < this->memberSize; i++) {
                newArr[i] = this->pWorker[i];
            }
        }
        // 添加新职工
        for (int i = 0; i < addNum; i++) {
            Worker *worker = NULL;
            int id;
            string name;
            int deptId;
            cout << "请输入第" << i << "位新职工的编号" << endl;
            cin >> id;
            cout << "请输入第" << i << "位新职工的姓名" << endl;
            cin >> name;
            cout << "请输入第" << i << "位新职工的部门编号" << endl;
            cout << "1.员工" << endl;
            cout << "2.经理" << endl;
            cout << "3.老板" << endl;
            cin >> deptId;
            switch (deptId) {
                case 1:
                    worker = new Employee(id, name, deptId);
                    break;
                case 2:
                    worker = new Manager(id, name, deptId);
                    break;
                case 3:
                    worker = new Boss(id, name, deptId);
                    break;
            }
            // 将新员工加入到数组
            newArr[this->memberSize + i] = worker;
        }
        // 释放旧空间
        delete[]this->pWorker;
        // 将员工数组指针指向新空间
        this->pWorker = newArr;
        // 更新现有员工数量
        this->memberSize = curSize;
    } else {
        cout << "输入有误" << endl;
    }
}

添加功能测试

  • main.cpp添加职工的位置调用添加的函数
wm.AddEmp();
  • 图示

在这里插入图片描述

文件交互–写文件文件

功能分析

  • 上面虽然实现了添加员工操作,但是数据保存在堆区,程序结束后堆区的数据还是会被清空,因此将添加的员工持久化到文件中

功能实现

  • workerManager.hpp中添加对应的头文件和成员函数
// 用于文件操作
#include <fstream>
// 定义写入文件
#define FILENAME "empFile.txt"


    // 保存文件
    void saveFile();
  • workerManager.cpp中实现保存文件函数,并在添加员工成功后调用保存文件操作
void WorkerManager::saveFile() {
    // 以写入的方式打开文件
    ofstream ofs;
    ofs.open(FILENAME, ios::out);
    for (int i = 0; i < this->memberSize; i++) {
        // 按行写入每一个员工信息
        ofs << this->pWorker[i]->m_id << " "
            << this->pWorker[i]->m_name << " "
            << this->pWorker[i]->m_deptId << endl;
    }
    // 关闭文件
    ofs.close();
}

// 调用保存文件
this->saveFile();

功能测试

  • 录入新职工信息

在这里插入图片描述

  • 文件查看

在这里插入图片描述

文件交互–读文件

功能分析

  • 虽然录入员工信息已经写入了文件,但是每次程序重启时并没有将文件中的用户加载到程序中来,因此需要使用读文件的操作,在程序启动时将文件中的数据加载到程序中来

  • 加载时存在以下三种情况

    • 文件不存在,此时处于未录入任何员工状态

    • 文件存在但没有数据,此时处于员工被清空状态

    • 文件存在且有数据,此时处于员工被正常录入情况,需要将文件中的数据加载到程序中

功能实现

  • 添加bool isEmptyFile;成员属性判断是否为空文件

  • 加载时三种不同的情况对应构造函数三种不同的赋值方式

  • workerManager.hpp中添加对应的成员函数声明

    // 获取成员数量
    int getEmpNum();

    // 初始化员工
    void initEmp();
  • workerManager.cpp中实
int WorkerManager::getEmpNum() {
    ifstream ifs;
    ifs.open(FILENAME, ios::in);
    if (!ifs.is_open()) {
        cout << "open file fail" << endl;
    }
    char buf[1024] = {0};
    int memberNum = 0;
    // 判断文件中有多少行,就有多少个员工
    while (ifs.getline(buf, sizeof(buf))) {
        memberNum++;
    }
    ifs.close();
    return memberNum;
}

WorkerManager::WorkerManager() {
    ifstream ifs;
    ifs.open(FILENAME, ios::in);
    if (!ifs.is_open()) {
        cout << "文件不存在" << endl;
        // 1.文件不存在
        // 初始化成员变量
        this->memberSize = 0;
        this->pWorker = NULL;
        this->isEmptyFile = true;
        // 关闭读文件流
        ifs.close();
        return;
    }
    // 文件存在
    char ch;
    ifs >> ch;
    // C++中判断文件是否为空采用向右读一个字节,调用ifs.eof()判断是否为真
    if (ifs.eof()) {
        cout << "文件存在但为空" << endl;
        // 2.文件不存在
        // 初始化成员变量
        this->memberSize = 0;
        this->pWorker = NULL;
        this->isEmptyFile = true;
        // 关闭读文件流
        ifs.close();
        return;
    }
    // 文件存在且不为空
    this->isEmptyFile = false;
    // 获取职工数量
    this->memberSize = this->getEmpNum();
    // 根据职工数量现在堆上开辟空间
    this->pWorker = new Worker *[this->memberSize];
    // 调用初始化成员将文件中的数据加载到内存中
    this->initEmp();
    return;
}


void WorkerManager::initEmp() {
    ifstream ifs;
    ifs.open(FILENAME, ios::in);
    if (!ifs.is_open()) {
        cout << "open file fail" << endl;
        return;
    }
    int id;
    string name;
    int deptId;
    Worker *worker = NULL;
    int index = 0;
    while (ifs >> id && ifs >> name && ifs >> deptId) {
        switch (deptId) {
            case 1:
                worker = new Employee(id, name, deptId);
                break;
            case 2:
                worker = new Manager(id, name, deptId);
                break;
            case 3:
                worker = new Boss(id, name, deptId);
                break;
        }
        this->pWorker[index] = worker;
        index++;
    }
    ifs.close();
}

显示职工信息

功能分析

  • 显示职工信息时,根据职工数量和职工数组,挨个遍历

  • 在遍历过程中判断deptId,根据多态的方式去调用不同职工的ShowWorkerInfo函数

功能实现

  • workerManager.hpp中添加成员函数声明

  • 代码示例

    // 显示职工信息
    void ShowEmpInfo();
  • workerManager.cpp中实现

  • 代码示例

void WorkerManager::ShowEmpInfo() {
    for (int i = 0; i < this->memberSize; i++) {
        Worker *worker = NULL;
        switch (this->pWorker[i]->m_deptId) {
            case 1:
                worker = new Employee(this->pWorker[i]->m_id, this->pWorker[i]->m_name, this->pWorker[i]->m_deptId);
                break;
            case 2:
                worker = new Manager(this->pWorker[i]->m_id, this->pWorker[i]->m_name, this->pWorker[i]->m_deptId);
                break;
            case 3:
                worker = new Boss(this->pWorker[i]->m_id, this->pWorker[i]->m_name, this->pWorker[i]->m_deptId);
                break;
        }
        worker->ShowWorkerInfo();
    }
}

删除职工

功能分析

  • 首先使用用户交互让用户输入要删除的员工id
  • 根据id查找员工是否存在,不存在直接返回,存在找到公所在位置,从内存中删除
  • 将删除后内存中的数据重新写文件

功能实现

  • workerManager.hpp中添加成员函数声明

  • 代码示例

    // 删除职工
    void DelEmp();
    
    // 根据id查找职工在数组中的位置
    int findIndexById(int id);
  • workerManager.cpp中实现

  • 代码示例

void WorkerManager::DelEmp() {
    int id;
    cout << "请输入要删除的员工编号" << endl;
    cin >> id;
    int index = this->findIndexById(id);
    if (index == -1) {
        return;
    }
    // 从前往后依次往前移动一个
    for (int i = index; i < this->memberSize - 1; i++) {
        this->pWorker[i] = this->pWorker[i + 1];
    }
    // 职工数减1
    this->memberSize--;
    // 重新写文件
    this->saveFile();
}

int WorkerManager::findIndexById(int id) {
    int i = 0;
    for (; i < this->memberSize; i++) {
        if (this->pWorker[i]->m_id == id) {
            break;
        }
    }
    // 没找到
    if (i == this->memberSize) {
        return -1;
    }
    return i;
}
  • 在添加职工的逻辑中添加判断职工编号是否已被占用逻辑
            while (true) {
                cin >> id;
                if (this->findIndexById(id) != -1) {
                    cout << "编号已存在,请重新输入" << endl;
                } else {
                    break;
                }
            }

修改职工

功能分析

  • 首先使用用户交互让用户输入要修改的员工id
  • 根据id查找员工是否存在,不存在直接返回,存在找到公所在位置
  • 通过交互让用户传入要修改的名字和部门编号
  • 将修改后内存中的数据重新写文件

功能实现

  • workerManager.hpp中添加成员函数声明

  • 代码示例

    // 删除工
    void UpdateEmp();
  • workerManager.cpp中实现

  • 代码示例

void WorkerManager::UpdateEmp() {
    int id;
    cout << "请输入要修改的员工编号" << endl;
    cin >> id;
    int index = this->findIndexById(id);
    if (index == -1) {
        cout << "员工不存在" << endl;
        return;
    }
    string name;
    int deptId;
    cout << "请输入要修改的名字" << endl;
    cin >> name;
    this->pWorker[index]->m_name = name;
    cout << "请输入要修改的部门编号" << endl;
    cin >> deptId;
    this->pWorker[index]->m_deptId = deptId;
    // 将修改后的数据重写文件
    this->saveFile();
    cout << "修改成功" << endl;
}

查找职工信息

需求分析

  • 查询方式

    • 根据id查询

    • 根据name查询

  • 处理流程

    • 用户交互让用户选择使用那种方式查询

    • 根据不同的方式查询对应数据所在内存数组的下标

    • 使用多态的方式判断员工类型

    • 输出对应员工信息

功能实现

  • workerManager.hpp中添加成员函数声明

  • 代码示例

    // 查找职工
    void FindEmp();

    // 根据名字查找职工在数组中的位置
    int findIndexByName(string name);
  • workerManager.cpp中实现

  • 代码示例

// 查找职工
void WorkerManager::FindEmp() {
    cout << "请选择查找方式" << endl;
    cout << "1.根据id查找" << endl;
    cout << "2.根据name查找" << endl;
    int choice;
    int index;
    cin >> choice;
    switch (choice) {
        case 1:
            cout << "请输入要查找的id: ";
            int id;
            cin >> id;
            index = this->findIndexById(id);
            break;
        case 2:
            cout << "请输入要查找的name: ";
            string name;
            cin >> name;
            index = this->findIndexByName(name);
            break;
    }
    if (index == -1) {
        cout << "查无此人" << endl;
        return;
    }
    Worker *worker = NULL;
    switch (this->pWorker[index]->m_deptId) {
        case 1:
            worker = new Employee(this->pWorker[index]->m_id, this->pWorker[index]->m_name,
                                  this->pWorker[index]->m_deptId);
            break;
        case 2:
            worker = new Manager(this->pWorker[index]->m_id, this->pWorker[index]->m_name,
                                 this->pWorker[index]->m_deptId);
            break;
        case 3:
            worker = new Boss(this->pWorker[index]->m_id, this->pWorker[index]->m_name,
                              this->pWorker[index]->m_deptId);
            break;
    }
    worker->ShowWorkerInfo();
    return;
}

// 根据名字查找职工在数组中的位置
int WorkerManager::findIndexByName(string name) {
    int i = 0;
    for (; i < this->memberSize; i++) {
        if (this->pWorker[i]->m_name == name) {
            break;
        }
    }
    // 没找到
    if (i == this->memberSize) {
        return -1;
    }
    return i;
}

排序

需求分析

  • 排序方式

    • 按照id升序排列

    • 按照id降序排列

  • 处理流程

    • 用户交互让用户选择使用升序还是降序

    • 按照用户输入排序内存数组

    • 排序后展示保存文件

功能实现

// 按照编号排序
void WorkerManager::SortById() {
    cout << "请选择排序方式:" << endl;
    cout << "1.升序排序" << endl;
    cout << "2.降序排序" << endl;
    int select;
    cin >> select;
    for (int i = 0; i < this->memberSize; i++)
    {
        int minOrMax = i;
        for (int j = i + 1; j < this->memberSize; j++)
        {
            if (select == 1) //升序
            {
                if (this->pWorker[minOrMax]->m_id > this->pWorker[j]->m_id)
                {
                    minOrMax = j;
                }
            }
            else  //降序
            {
                if (this->pWorker[minOrMax]->m_id < this->pWorker[j]->m_id)
                {
                    minOrMax = j;
                }
            }
        }

        if (i != minOrMax)
        {
            Worker * temp = this->pWorker[i];
            this->pWorker[i] = this->pWorker[minOrMax];
            this->pWorker[minOrMax] = temp;
        }

    }
    cout << "排序成功,结果如下" << endl;
    this->ShowEmpInfo();
    this->saveFile();
}

清空文件

需求分析

  • 直接将数量置空

  • 将内存中的数据加载到文件

功能实现

  • workerManger.hpp中声明成员函数
void WorkerManager::ClearEmp();
  • workerManger.cpp中实现成员函数
void WorkerManager::ClearEmp() {
    this->memberSize = 0;
    this->saveFile();
}
  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

double_happiness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值