恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

C++分步实现职工管理系统详解

  • 首页
  • 资讯中心
  • /
  • C++分步实现职工管理系统详解

相关资讯

应用开发流程管理:破解效率与风险平衡的实践指南 2026/10/15 4:15:14
Intel 82599手册实战:从寄存器到RSS的网卡调试指南 2026/10/15 4:15:14
西门子冗余系统配置实战:从选型组态到无扰切换的避坑指南 2026/10/15 4:15:14

最新资讯

高阶RAG实战:从知识库到认知协作者的四大跃迁
C++ std::list接口精讲:splice、迭代器与erase避坑指南
JY901姿态传感器上手实测:从开箱到读出第一帧角度数据
光伏储能系统核心技术解析与工程实践
AI 浪潮下,从业者如何搭建高效信息获取体系|信源清单与信息甄别方法论
C语言从零手写webserver:epoll+线程池+HTTP解析实战与面试避坑指南

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

C++分步实现职工管理系统详解

发布时间:2026/10/15 4:15:14
C++分步实现职工管理系统详解 前言职工管理系统是 C 面向对象阶段最经典的练手项目一个菜单、几个增删改查选项、三个不同岗位的职工、一份文本数据文件。麻雀虽小它把封装、继承、多态、智能指针、文件流、标准算法全都串了一遍所以几乎每本教材都会安排它。但很多人做完之后的感觉是抄完了没懂把关键选择当成规定动作比如基类析构要加 virtual——记住要加却不知道不加会怎样。这个项目里几乎每一处设计都能问一句为什么答上来了才算真的做完。另一个容易被带偏的点是持久化的判别符不少教程存文件时把岗位名称当类型标识写进去读回来再按字符串比较只要有一天改了界面文案历史数据就全部读不出来。本文改用整数类型编号。下面按拆需求 → 定类层次 → 写管理类 → 拼完整程序四步走最后给一份完整的单文件实现标准 C17GCC 13 / Clang 17 / MSVC 19.3x 均可编译。一、需求拆解与类设计先把功能列清楚避免写着写着加戏编号功能关键点1添加职工编号唯一性校验按岗位创建不同的子类对象2显示全部职工多态调用同一个基类指针调出不同的岗位名和职责3删除职工按编号查找用std::find_if4保存 / 读取文本格式一行一条启动时自动加载0退出退出前保存类设计只有一句话用一个抽象基类Worker描述所有职工都有的东西用派生类描述岗位差异。编号、姓名、部门放在基类的私有成员里岗位名称、岗位职责、持久化类型编号做成纯虚函数派生类各自实现。这样WorkerManager只需要维护一组Worker完全不用写如果是经理就怎样这种分支。新增一个岗位只需要加一个派生类和在创建时多一个case显示、删除、保存、加载的逻辑一行都不用改。二、职工类层次抽象基类加三个派生类基类需要写的是一个构造函数、一个虚析构和三个纯虚函数class Worker {public:Worker(int id, std::string name, int deptId); // 函数体见第四节完整代码virtual ~Worker() default; // 必须有virtual std::string jobName() const 0;virtual std::string duty() const 0;virtual int jobType() const 0; // 1员工 2经理 3老板int id() const noexcept;const std::string name() const noexcept;int deptId() const noexcept;private:int id_;std::string name_;int deptId_;};派生类没有额外数据成员可以直接用using Worker::Worker;继承基类构造函数省掉三份重复的构造函数体class Employee : public Worker {public:using Worker::Worker;std::string jobName() const override { return 普通员工; }std::string duty() const override { return 完成经理交给的任务; }int jobType() const override { return 1; }};Manager和Boss结构完全一样只有三个返回值不同。两个值得展开的点第一virtual ~Worker() default;不是可选项。后面我们会在std::vectorstd::unique_ptrWorker里存放Employee对象。unique_ptrWorker析构时执行delete ptr指针的静态类型是Worker*、动态类型是Employee*。基类析构不是虚函数时只调用Worker的析构函数派生类部分不会被析构。这是未定义行为UB标准不保证任何结果实践中通常表现为资源泄漏或运行时崩溃。第二用unique_ptr而不是裸指针。教材里常用Worker** workers_;加手工new/delete中途抛异常或忘记释放就会泄漏unique_ptr把释放责任绑到对象生命周期上WorkerManager的析构函数一行都不用写。代价是它不可拷贝——那本来也不该拷贝。三、WorkerManager菜单驱动的主循环输入处理抽成一个小函数避免每个功能都写一遍错误处理int readInt(const std::string prompt) {int value 0;while (true) {std::cout prompt;if (std::cin value) return value;std::cin.clear(); // 退出失败状态std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n); // 丢掉坏输入std::cout 输入无效请输入一个整数。\n;}}clear()加ignore()这个组合是必须的。用户输入abc而程序用读整数时流会进入失败状态此后所有读取都立刻失败不clear()就退不出失败状态不ignore()掉那一行下一次读到的还是同样的abc于是屏幕上疯狂滚动输入无效。另配一个readWord逻辑类似读一个不含空格的词。主循环唯一容易写错的地方是退出分支case 0:save();std::cout 数据已保存系统退出。\n;return; // 用 return不要用 breakbreak只会跳出switch然后回到while里继续循环永远退不出去。这是这类菜单程序最常见的手误。创建对象时用switch把整数岗位映射到具体子类这是全程序唯一需要知道有哪些岗位的地方其余逻辑全部通过基类接口完成。四、完整代码与文件持久化// worker_manager.cpp —— 职工管理系统C17单文件版#include algorithm#include fstream#include iomanip#include iostream#include limits#include memory#include string#include utility#include vectorclass Worker {public:Worker(int id, std::string name, int deptId): id_(id), name_(std::move(name)), deptId_(deptId) {}virtual ~Worker() default;virtual std::string jobName() const 0;virtual std::string duty() const 0;virtual int jobType() const 0;int id() const noexcept { return id_; }const std::string name() const noexcept { return name_; }int deptId() const noexcept { return deptId_; }private:int id_;std::string name_;int deptId_;};class Employee : public Worker {public:using Worker::Worker;std::string jobName() const override { return 普通员工; }std::string duty() const override { return 完成经理交给的任务; }int jobType() const override { return 1; }};class Manager : public Worker {public:using Worker::Worker;std::string jobName() const override { return 经理; }std::string duty() const override { return 完成老板交给的任务并下发任务给员工; }int jobType() const override { return 2; }};class Boss : public Worker {public:using Worker::Worker;std::string jobName() const override { return 老板; }std::string duty() const override { return 管理公司所有事务; }int jobType() const override { return 3; }};namespace {int readInt(const std::string prompt) {int value 0;while (true) {std::cout prompt;if (std::cin value) return value;std::cin.clear();std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n);std::cout 输入无效请输入一个整数。\n;}}std::string readWord(const std::string prompt) {std::string value;std::cout prompt;if (!(std::cin value)) {std::cin.clear();std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n);}return value;}} // namespaceclass WorkerManager {public:WorkerManager() { load(); }void run() {while (true) {showMenu();const int choice readInt(请选择: );switch (choice) {case 1: addWorker(); break;case 2: showAll(); break;case 3: removeWorker(); break;case 4: save(); break;case 0:save();std::cout 数据已保存系统退出。\n;return;default:std::cout 没有这个选项请重新输入。\n;break;}}}private:// C17 起静态 constexpr 成员隐含 inline不需要类外再定义一次static constexpr const char* kDataFile worker.txt;void showMenu() const {std::cout \n 职工管理系统 \n 1. 添加职工\n 2. 显示全部职工\n 3. 删除职工\n 4. 保存到文件\n 0. 退出并保存\n\n;}void addWorker() {const int id readInt(请输入职工编号: );for (const auto w : workers_) {if (w-id() id) {std::cout 编号 id 已存在。\n;return;}}const std::string name readWord(请输入职工姓名: );if (name.empty()) {std::cout 姓名不能为空已取消添加。\n;return;}const int type readInt(岗位1 普通员工 / 2 经理 / 3 老板: );const int deptId readInt(部门编号: );std::unique_ptrWorker w;switch (type) {case 1: w std::make_uniqueEmployee(id, name, deptId); break;case 2: w std::make_uniqueManager(id, name, deptId); break;case 3: w std::make_uniqueBoss(id, name, deptId); break;default:std::cout 岗位无效已取消添加。\n;return;}workers_.push_back(std::move(w));std::cout 添加成功。\n;}void showAll() const {if (workers_.empty()) {std::cout 目前没有任何职工记录。\n;return;}std::cout std::left std::setw(8) 编号 std::setw(16) 姓名 std::setw(8) 部门 std::setw(14) 岗位 职责\n;for (const auto w : workers_) {std::cout std::left std::setw(8) w-id() std::setw(16) w-name() std::setw(8) w-deptId() std::setw(14) w-jobName() w-duty() \n;}std::cout 共 workers_.size() 条记录。\n;}void removeWorker() {const int id readInt(请输入要删除的职工编号: );const auto it std::find_if(workers_.begin(), workers_.end(),[id](const std::unique_ptrWorker w) { return w-id() id; });if (it workers_.end()) {std::cout 没有找到编号为 id 的职工。\n;return;}workers_.erase(it);std::cout 删除成功。\n;}void save() const {std::ofstream ofs(kDataFile, std::ios::out | std::ios::trunc);if (!ofs) {std::cerr 无法写入数据文件 kDataFile \n;return;}for (const auto w : workers_) {// 每行四条编号 姓名 部门 岗位类型编号ofs w-id() w-name() w-deptId() w-jobType() \n;}std::cout 已保存 workers_.size() 条记录。\n;}void load() {std::ifstream ifs(kDataFile);if (!ifs) return; // 首次运行没有数据文件属于正常情况int id 0;std::string name;int deptId 0;int type 0;// 用流状态做循环条件任何一项读失败都会结束循环while (ifs id name deptId type) {switch (type) {case 1: workers_.push_back(std::make_uniqueEmployee(id, name, deptId)); break;case 2: workers_.push_back(std::make_uniqueManager(id, name, deptId)); break;case 3: workers_.push_back(std::make_uniqueBoss(id, name, deptId)); break;default: break;}}}std::vectorstd::unique_ptrWorker workers_;};int main() {WorkerManager mgr;mgr.run();return 0;}把上面这段存成worker_manager.cpp用g -stdc17 -Wall -Wextra -o worker_manager worker_manager.cpp编译即可。数据文件是纯文本、一行一条形如1001 张三 10 1。为什么用jobType()返回整数而不是直接存岗位名称中文文案一改历史文件就再也对不上号而且std::cin name读的是一个词中文串里以后有空格还会解析错位。整数编号稳定、与界面文案解耦这是持久化格式设计的基本原则。常见坑点1. 基类析构函数不是虚函数UBclass Worker { public: ~Worker() {} virtual std::string duty() const 0; }; // ❌std::unique_ptrWorker p std::make_uniqueEmployee(1, 张三, 10);// p 析构时 delete (Worker*)p派生类部分不被析构 —— UBclass Worker { public: virtual ~Worker() default; }; // ✅2. 把派生类对象按值存进容器发生对象切片object slicingstd::vectorWorker v; // ❌ Worker 是抽象类根本装不进去std::vectorstd::unique_ptrWorker v; // ✅ 存指针多态保留完整3.std::move之后再使用那个unique_ptrauto w std::make_uniqueEmployee(1, 张三, 10);workers_.push_back(std::move(w));std::cout w-name(); // ❌ w 已被置空解引用是 UB多数实现直接崩std::cout workers_.back()-name(); // ✅ 用移动后的目标4. 用operator读姓名姓名里有空格就被截断std::cin name; // ❌ 输入 张 三只读到 张std::getline(std::cin std::ws, name); // ✅ 读整行并吃掉前导空白用getline时要注意前面若刚用读过整数缓冲区里还留着一个换行符得先用std::ws或ignore()处理掉。5. 输入失败后不clear()ignore()程序进入死循环int n;while (!(std::cin n)) { std::cout 请重新输入\n; } // ❌ 流一直失败读一万次还是失败while (!(std::cin n)) { std::cin.clear(); std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n); } // ✅总结步骤产出关键决策类层次Worker 三个派生类共有数据放基类差异做成纯虚函数生命周期virtual ~Worker() default;缺了它通过基类指针删除就是 UB存储方式std::vectorstd::unique_ptrWorker用智能指针代替手工 new/delete输入处理readInt/readWord失败要clear()ignore()主循环switchreturn退出退出用return而不是break持久化文本四列类型用整数判别符必须与界面文案解耦这个项目真正值得带走的有三件多态让新增一种职工只需要加一个派生类unique_ptr让忘记释放从可能事故变成编译期不可能而持久化判别符用整数不用文案是一条通用到任何项目都成立的经验。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号