对员工信息管理程序的重构-第一周作业
(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)总结
难点:尽管原代码已较为完善,但是仍存在部分问题。在重构时需要从头理解原代码的设计意图
除此之外,不同人的代码书写风格略有不同,在缺少注释的情况下理解代码略有难度
耗时点:科室平均薪资的重构涉及数据结构变更,需确保旧数据兼容性。
逆向工程思考:通过分析代码执行流程和数据流向,发现隐藏的内存泄漏和逻辑漏洞,强调代码可维护性的重要性。

浙公网安备 33010602011771号