对员工信息管理程序的重构-第一周作业

(1)原项目来源
同学大一下cpp期末作业

(2)运行环境及主要功能截图
visual studio 2022

主界面

添加员工

修改员工信息

查询员工

删除员工

以下为原代码

点击查看代码
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <sstream>//使用stringstream
#include <stdlib.h>//白底黑字
using namespace std;
class Employee {
private:
    int empId;
    string name;
    char gender;
    string phoneNumber;
    string department;
    double salary;
public:
	Employee(){}
    Employee(int id, string n, char g, string phone, string dept, double sal)
        : empId(id), name(n), gender(g), phoneNumber(phone), department(dept), salary(sal) {}
    void display() {//员工信息打印函数
        cout << "员工ID: " << empId << endl;
        cout << "姓名: " << name << endl;
        cout << "性别: " << gender << endl;
        cout << "手机号码: " << phoneNumber << endl;
        cout << "科室: " << department << endl;
        cout << "薪资: " << salary << endl;
        cout << "---------------------------------" << endl;
    }
    void modifyEmployee(char g, string phone, string dept, double sal) {//员工信息修改函数
	        gender = g;
	        phoneNumber = phone;
	        department = dept;
	        salary = sal;
    }
    int getEmpId() {
        return empId;
    }
    string getName() {
        return name;
    }
    char getGender(){
		return gender;
	}
	string getPhoneNumber(){
		return phoneNumber;
	}
	string getDepartment(){
		return department;
	}
	double getSalary(){
		return salary;
	}
    friend ostream& operator<<(ostream& out, const Employee& employee);
	friend istream& operator>>(istream& in, Employee& employee);
};
ostream& operator<<(ostream& out, const Employee& employee) {
    out << employee.empId << " "
        << employee.name << " "
        << employee.gender << " "
        << employee.phoneNumber << " "
        << employee.department << " "
        << employee.salary;
    	return out;
}
istream& operator>>(istream& in, Employee& employee) {
    in >> employee.empId >>
          employee.name >>
          employee.gender >>
          employee.phoneNumber >>
          employee.department >>
          employee.salary;
    	return in;
}
class EmployeeManagementSystem {
private:
	Employee *employees[100];//employees[]是一个Employee类的数组,能储存100个Employee对象的指针
	int numEmployees=0;
public:
    EmployeeManagementSystem() : numEmployees(0) {}
    // 添加员工信息
    void addEmployee() {
        int id;
        string name, phone, dept;
        char gender;
        double salary;
        cout << "请输入员工的ID:";
        cin >> id;
        cout << "请输入员工的姓名: ";
        cin.ignore(); // 默认为跳过上个输入留下的换行符
        getline(cin, name);
        cout << "请输入员工的性别(M/F): ";
        cin >> gender;
        cout << "请输入员工的手机号码: ";
        cin >> phone;
        cout << "请输入员工的科室: ";
        cin >> dept;
        cout << "请输入员工的薪资: ";
        cin >> salary;
        employees[numEmployees] = new Employee(id, name, gender, phone, dept, salary);
		numEmployees++;
        cout << "员工信息成功添加." << endl;
        saveToFile();
    }
    // 删除员工信息
    void deleteEmployee(int empId) {
        bool found = false;
        for (int i = 0; i < numEmployees; ++i) {
            if (employees[i]->getEmpId() == empId) {
                delete employees[i];
                employees[i] = employees[numEmployees - 1];
                numEmployees--;
                found = true;
                cout << "ID为 " << empId << " 的员工信息删除成功." << endl;
                break;
            }
        }
        if (!found) {
            cout << "没有找到该员工." << endl;
        }
        saveToFile();
    }
    // 修改员工信息
    void updateEmployee(int empId) {
        bool found = false;
        char gender;
        string phone, dept;
        double salary;
        for (int i = 0; i < numEmployees; ++i) {
            if (employees[i]->getEmpId() == empId) {
                cout << "输入新的性别(M/F): ";
                cin >> gender;
                cout << "输入新的手机号码: ";
                cin >> phone;
                cout << "输入新的科室: ";
                cin >> dept;
                cout << "输入新的薪资: ";
                cin >> salary;
                employees[i]->modifyEmployee(gender, phone, dept, salary);
                found = true;
                cout << "id为 " << empId << " 员工更新成功" << endl;
                break;
            }
        }
        if (!found) {
            cout << "没有找到该员工." << endl;
        }
        saveToFile();
    }
    // 按姓名查询员工信息
    void searchEmployeeByName(string name) {
        bool found = false;
        for (int i = 0; i < numEmployees; ++i) {
            if (employees[i]->getName() == name) {
                employees[i]->display();
                found = true;
                break;
            }
        }
        if (!found) {
            cout << "名字为" << name << "的员工没有找到" << endl;
        }
        saveToFile();
    }
    // 按科室统计平均工资
 	void add() {
         if (numEmployees == 0) {
             cout << "没有员工可以计算平均工资." << endl;
             return;
         }
         double totalSalary = 0;
         int count = 0;
         string currentDept = employees[0]->getDepartment();
         cout << "按科室划分的平均工资:" << endl;
         for (int i = 0; i < numEmployees; ++i) {
             if (employees[i]->getDepartment() == currentDept) {
                 totalSalary += employees[i]->getSalary();
                 count++;
             } else {
                 cout << "科室: "<<currentDept << ", 平均薪资: " << totalSalary / count << endl;
                 currentDept = employees[i]->getDepartment();
                 totalSalary = employees[i]->getSalary();
                 count = 1;
             }
         }
         cout << "科室: " << currentDept << ", 平均薪资: " << totalSalary / count << endl;
         saveToFile();
     }
	 //按科室查找员工
	 void researchEmployeeByDepartment() {
	     string dept;
	     cout << "请输入要查询的科室名称:";
	     cin >> dept;
	     bool found = false;
	     // 遍历所有员工,查找特定科室的员工并输出
	     for (int i = 0; i < numEmployees; ++i) {
	         if (employees[i]->getDepartment() == dept) {
	             found = true;
	             cout << "科室: " << dept << " 的员工有 " << employees[i]->getName() << endl;
	         }
	     }
	     if (!found) {
	         cout << "未找到任何属于科室 '" << dept << "' 的员工." << endl;
	     }
	     saveToFile();
	 }
	 //员工信息读取函数
    void readFromFile() {  
	   	ifstream inFile("employees.txt");  
	    if (!inFile) {  
			cerr << "无法打开文件 'employees.txt' 进行读取." << endl;  
	        	return;  
	    	}
	    	string line;  
	    	while (getline(inFile, line) && numEmployees < 100) {
	        	Employee* emp = new Employee(); // 创建新的 Employee 对象  
	        	stringstream ss(line);  //将从文件中读取的每一行数据 line 放入 stringstream 对象 ss 中,以便于后续使用 >> 操作符从中提取数据。
	        	ss >> *emp;  //将数据存储在Employee的对象中,使用了重载运算符
	        	employees[numEmployees++] = emp; // 将创建的员工对象指针存储到数组中 
	    	}
	    	inFile.close();
    	}  
    // 保存员工信息到文件
    void saveToFile() {
        ofstream outFile("employees.txt");
        if (!outFile) {
		        cerr << "无法打开文件 'employees.txt' 进行保存." << endl;
		        return;
        }
		    for (int i = 0; i < numEmployees; ++i) {
		        outFile << *employees[i]; //employees 是一个数组,存储了指向 Employee 对象的指针,employees[i] 表示数组中第 i 个元素,即指向 Employee 对象的指针。*employees[i] 则是解引用操作,表示获取指针 employees[i] 所指向的 Employee 对象。
		        if (i != numEmployees - 1)
		            outFile << endl; // 换行,如果不是最后一个对象
		    }
		    outFile.close();
		}
};
int main() {
	system("color F0");
    EmployeeManagementSystem msystem;
    msystem.readFromFile();//从文件中读取员工的信息
    int choice;
    do {
        cout << "公司员工信息管理系统:" << endl;
        cout << "1. 添加职工信息" << endl;
        cout << "2. 删除职工信息" << endl;
        cout << "3. 修改职工信息" << endl;
        cout << "4. 按名字查询职工信息" << endl;
        cout << "5. 按科室计算平均薪资" << endl;
        cout << "6. 按科室查询员工名字"<<endl;
        cout << "0. 退出系统" << endl;
        cout << "请输入你的选择: ";
        cin >> choice;
        switch (choice) {
            case 1:
                msystem.addEmployee();
                break;
            case 2: {
                int empId;
                cout << "请输入员工的编号来删除员工信息: ";
                cin >> empId;
                msystem.deleteEmployee(empId);
                break;
            }
            case 3: {
                int empId;
                cout << ":请输入员工的编号来修改员工信息 ";
                cin >> empId;
                msystem.updateEmployee(empId);
                break;
            }
            case 4: {
                string name;
                cout << "请输入员工姓名来查询: ";
                cin.ignore(); // 忽略上一个输入操作后留下的换行符
                getline(cin, name);
                msystem.searchEmployeeByName(name);
                break;
            }
            case 5:
                msystem.add();
                break;
            case 6:
            	msystem.researchEmployeeByDepartment();
            case 0:
                cout << "退出公司员工信息管理系统." << endl;
                break;
            default:
                cout << "无效的选择,请重新输入" << endl;
        }
    } while (choice != 0);
    return 0;
}

(3)主要问题列表及改进方案
01内存泄漏
问题:EmployeeManagementSystem类未释放动态分配的Employee对象。
改进:添加析构函数,释放内存。

02输入验证缺失
问题:性别、电话号码未做合法性检查。
改进:增加输入校验逻辑(如性别仅允许'M'/'F',电话号码需为数字)。

03科室平均薪资计算逻辑错误
问题:原代码假设同一科室员工连续存储,导致统计错误。
改进:改用map<string, vector>分组计算。

04函数命名不清晰
问题:计算平均薪资的函数名为add(),易误解。
改进:重命名为calculateAverageSalaryByDepartment()。

05文件处理缺陷
问题:文件不存在时未创建新文件,可能导致保存失败。
改进:读取时若文件不存在,则初始化空文件。

06员工ID重复问题
问题:添加员工时未检查ID唯一性。
改进:遍历现有员工ID,确保唯一性。

07菜单逻辑错误
问题:case 6缺少break,导致直接退出。
改进:补充break语句。

(4)新代码(改进部分)

// 改进1:添加析构函数释放内存

点击查看代码
class EmployeeManagementSystem {
public:
    ~EmployeeManagementSystem() {
        for (int i = 0; i < numEmployees; ++i) {
            delete employees[i];
        }
    }
};

// 改进2:输入验证

点击查看代码
void addEmployee() {
    // ... 输入ID后增加唯一性检查
    for (int i = 0; i < numEmployees; ++i) {
        if (employees[i]->getEmpId() == id) {
            cout << "员工ID已存在!" << endl;
            return;
        }
    }
    // 性别验证
    if (gender != 'M' && gender != 'F') {
        cout << "性别输入错误!" << endl;
        return;
    }
}

// 改进3:科室平均薪资计算逻辑

点击查看代码
void calculateAverageSalaryByDepartment() {
    map<string, vector<double>> deptSalaries;
    for (int i = 0; i < numEmployees; ++i) {
        string dept = employees[i]->getDepartment();
        deptSalaries[dept].push_back(employees[i]->getSalary());
    }
    for (auto& pair : deptSalaries) {
        double total = 0;
        for (double sal : pair.second) total += sal;
        cout << "科室: " << pair.first 
             << ", 平均薪资: " << total / pair.second.size() << endl;
    }
}

// 改进4:文件读取时创建空文件

点击查看代码
void readFromFile() {
    ifstream inFile("employees.txt");
    if (!inFile) {
        ofstream createFile("employees.txt"); // 创建空文件
        return;
    }
    // ... 原读取逻辑
}

// 改进5:修复菜单switch-case错误

点击查看代码
switch (choice) {
    case 6:
        msystem.researchEmployeeByDepartment();
        break; // 补充break
    // ... 其他case
}

其余部分对于缩进或是其它无关紧要部分进行了修改,故不再列出
以下为改进后截图
菜单

添加员工

按姓名查询员工

按科室查询员工

按科室计算平均薪资

删除员工

修改员工信息

(5)总结

难点:尽管原代码已较为完善,但是仍存在部分问题。在重构时需要从头理解原代码的设计意图
除此之外,不同人的代码书写风格略有不同,在缺少注释的情况下理解代码略有难度

耗时点:科室平均薪资的重构涉及数据结构变更,需确保旧数据兼容性。

逆向工程思考:通过分析代码执行流程和数据流向,发现隐藏的内存泄漏和逻辑漏洞,强调代码可维护性的重要性。

posted @ 2025-02-24 22:57  GeminiKun  阅读(30)  评论(0)    收藏  举报