C++实战:面向对象编程与数据结构在快递柜管理系统中的应用

发布时间:2026/7/30 10:39:14

C++实战:面向对象编程与数据结构在快递柜管理系统中的应用 1. 项目概述与核心价值最近在整理过往的项目经验发现一个挺有意思的实践案例用C实现一个完整的快递柜管理系统。这听起来像是个学校课程设计但实际做下来你会发现它麻雀虽小五脏俱全几乎涵盖了C面向对象编程、数据结构、文件I/O、甚至是简单状态机设计的核心知识点。对于想从“语法学习者”转向“项目实践者”的C开发者来说这是一个绝佳的练手项目。它不涉及复杂的网络通信或图形界面当然你可以用Qt去扩展专注于用纯C解决一个具体的业务逻辑问题非常适合用来巩固基础理解如何将现实需求转化为清晰的类设计和高效的代码结构。这个系统的核心就是模拟我们小区里常见的那些快递柜。用户快递员和收件人通过一个终端界面进行操作背后则是一套管理快递柜格口状态、存储取件信息、处理超时逻辑的“大脑”。实现它你需要思考几个关键问题如何用代码表示一个个物理格口及其状态空闲、占用、待取、故障如何高效地分配和回收格口如何持久化存储订单数据即使程序重启也不会丢失这些问题的答案就构成了这个项目的骨架。接下来我会带你从设计思路到代码实现完整地走一遍这个流程分享我在实现过程中踩过的坑和总结的经验。2. 系统核心设计与类结构拆解在动手写代码之前花时间进行清晰的设计是避免后期重构的关键。对于快递柜管理系统我们需要抽象出几个核心的实体和它们之间的关系。2.1 核心实体抽象与类设计首先最基础的实体是快递柜格口。每个格口有唯一的编号、大小规格如小、中、大、当前状态以及可能存放的包裹信息。这里状态管理是重点我通常用一个枚举类来定义。// 格口状态枚举 enum class LockerStatus { EMPTY, // 空闲 OCCUPIED, // 已占用存放了包裹 AWAITING_PICKUP, // 待取件已通知收件人 FAULTY // 故障 }; // 格口大小枚举 enum class LockerSize { SMALL, MEDIUM, LARGE };有了状态定义CabinetLocker类就呼之欲出了。这个类应该是一个“纯数据”的实体类主要包含属性和一些基本的状态判断方法。class CabinetLocker { private: int id_; // 格口唯一ID LockerSize size_; LockerStatus status_; std::string parcelId_; // 当前存放的包裹单号 std::chrono::system_clock::time_point storeTime_; // 存入时间 // ... 其他属性如位置信息等 public: CabinetLocker(int id, LockerSize size); // Getter 和 Setter bool isEmpty() const { return status_ LockerStatus::EMPTY; } bool isAvailableForSize(LockerSize reqSize) const; // 分配包裹、取出包裹、标记故障等方法 bool assignParcel(const std::string parcelId, LockerSize parcelSize); std::string retrieveParcel(); // 取出包裹返回包裹单号并置空格口 };注意关于时间处理我强烈建议使用 C11 的chrono库。它比传统的time_t更安全、更强大便于进行超时计算比如包裹存放超过24小时开始计费。storeTime_的类型可以是std::chrono::system_clock::time_point。接下来是包裹订单。它关联了格口、快递员、收件人以及时间线。这个类负责记录一次完整的存、取件生命周期。class ParcelOrder { private: std::string orderId_; // 订单号 std::string parcelId_; // 包裹单号快递单号 int lockerId_; // 使用的格口ID std::string courierId_; // 快递员ID std::string receiverPhone_; // 收件人手机号 std::chrono::system_clock::time_point storedTime_; std::chrono::system_clock::time_point pickedUpTime_; bool isPickedUp_; // ... 可能还有费用信息 public: ParcelOrder(std::string parcelId, int lockerId, std::string courierId, std::string receiverPhone); void markAsPickedUp(); // 计算存放时长、是否超时等方法 std::chrono::hours getStorageDuration() const; bool isOverdue(int overdueHours 24) const; };最后我们需要一个管理系统类作为总控制器。这是整个项目的“大脑”它管理着所有格口和订单的集合并提供核心的业务接口。这里涉及到数据结构的选型。class LockerManagementSystem { private: std::vectorCabinetLocker lockers_; // 所有格口 std::unordered_mapstd::string, ParcelOrder orders_; // 订单号到订单的映射 std::unordered_mapstd::string, int parcelToLockerMap_; // 包裹单号到格口ID的映射用于快速查找 // 文件路径用于数据持久化 std::string lockersDataFile_; std::string ordersDataFile_; public: LockerManagementSystem(); bool initializeFromFile(); // 从文件加载数据 bool saveToFile() const; // 保存数据到文件 // 核心业务方法 int findAvailableLocker(LockerSize requiredSize); bool storeParcel(const std::string parcelId, LockerSize size, const std::string courierId, const std::string receiverPhone); bool pickupParcel(const std::string parcelId, const std::string verificationCode); // 假设有验证码 std::vectorint getOverdueLockers(int thresholdHours) const; // ... 其他管理方法如查询状态、标记故障等 };实操心得在LockerManagementSystem中我使用了std::unordered_map来存储订单和包裹-格口映射。选择unordered_map而非map是因为在快递柜场景下我们更频繁的操作是通过键订单号、包裹单号进行快速查找对顺序没有要求unordered_map的平均 O(1) 时间复杂度更有优势。vector用于存储格口因为格口ID通常是连续或易于索引的顺序访问和随机访问都很高效。2.2 数据持久化策略选择程序不能每次重启都清空数据所以持久化是必须的。对于这种规模的项目使用关系型数据库如SQLite是专业选择但为了更纯粹地聚焦C核心我们也可以使用文件序列化。这里有两种常见选择文本格式如JSON、XML可读性好易于调试。可以使用像nlohmann/json这样的第三方库非常方便。二进制格式存储紧凑读写快。可以用C标准库的fstream配合reinterpret_cast需谨慎或直接为每个类实现serialize/deserialize方法。我个人的选择是JSON。原因很简单开发调试阶段能直接打开文件查看数据内容定位问题效率极高。nlohmann/json库只需要一个头文件集成简单API也非常直观。// 示例将 CabinetLocker 对象转换为 JSON nlohmann::json lockerJson; lockerJson[id] locker.id_; lockerJson[size] static_castint(locker.size_); // 枚举转int存储 lockerJson[status] static_castint(locker.status_); lockerJson[parcelId] locker.parcelId_; // 时间点需要特殊处理可以存储为时间戳字符串 auto time_t_store std::chrono::system_clock::to_time_t(locker.storeTime_); lockerJson[storeTime] std::ctime(time_t_store); // 或存储为ISO8601字符串 // 反过来从JSON构建对象 locker.id_ lockerJson[id].getint(); locker.size_ static_castLockerSize(lockerJson[size].getint()); // ... 其他字段在LockerManagementSystem的saveToFile和initializeFromFile方法中就需要遍历lockers_和orders_将它们全部转换为JSON数组然后写入文件或从文件读取并重建对象。踩坑记录时间time_point的序列化是个小坑。直接存储时间戳time_t是最简单的但要注意平台兼容性。我更喜欢存储为ISO 8601格式的字符串如”2023-10-27T10:30:00Z“可读性和通用性都更好可以使用std::put_time和std::get_time进行格式化与解析。3. 核心业务流程的代码实现与详解设计好骨架后我们来填充血肉实现最关键的几个业务流程存件、取件和状态查询。3.1 存件流程的实现存件是快递员发起的操作。核心逻辑是根据包裹大小找到一个合适的空闲格口创建订单更新格口状态并保存所有数据。bool LockerManagementSystem::storeParcel(const std::string parcelId, LockerSize size, const std::string courierId, const std::string receiverPhone) { // 1. 检查包裹是否已存在防止重复存入 if (parcelToLockerMap_.find(parcelId) ! parcelToLockerMap_.end()) { std::cerr 错误包裹单号 parcelId 已存在 std::endl; return false; } // 2. 查找可用格口 int availableLockerId findAvailableLocker(size); if (availableLockerId -1) { std::cerr 错误没有找到适合尺寸的可用格口。 std::endl; return false; } // 3. 获取格口引用并分配包裹 CabinetLocker locker lockers_[availableLockerId]; // 假设lockers_索引即ID if (!locker.assignParcel(parcelId, size)) { std::cerr 错误格口分配失败。 std::endl; return false; } // 4. 创建订单记录 std::string orderId generateOrderId(); // 生成唯一订单号 ParcelOrder newOrder(parcelId, availableLockerId, courierId, receiverPhone); orders_.emplace(orderId, std::move(newOrder)); // 使用emplace原地构造效率更高 // 5. 更新快速查找映射 parcelToLockerMap_[parcelId] availableLockerId; // 6. 生成取件码简化版这里用随机数 std::string pickupCode generateVerificationCode(); // 在实际项目中这里应该调用短信服务接口发送取件码给 receiverPhone std::cout 包裹 parcelId 已存入格口 availableLockerId 。取件码 pickupCode 请通知收件人 std::endl; // 7. 立即保存数据到文件防止数据丢失 if (!saveToFile()) { std::cerr 警告数据保存失败但业务操作已成功。 std::endl; // 根据业务要求这里可能需要回滚操作或抛出异常 } return true; }findAvailableLocker函数的实现策略值得讨论。最简单的就是遍历lockers_向量找到第一个状态为空闲且尺寸大于等于需求尺寸的格口。但为了提高效率可以考虑按尺寸将格口分组管理。int LockerManagementSystem::findAvailableLocker(LockerSize requiredSize) { for (auto locker : lockers_) { // isAvailableForSize 方法内部判断 statusEMPTY 且 sizerequiredSize if (locker.isAvailableForSize(requiredSize)) { return locker.getId(); // 返回格口ID } } return -1; // 未找到 }注意事项这里的“适合尺寸”逻辑是“格口尺寸 包裹尺寸”。你也可以实现更复杂的策略比如优先分配尺寸最匹配的格口避免大包裹占用小格口这需要遍历时记录当前最匹配的格口ID。3.2 取件流程与验证逻辑取件由收件人操作需要验证取件码或手机号。核心是验证通过后更新订单和格口状态。bool LockerManagementSystem::pickupParcel(const std::string parcelId, const std::string inputCode) { // 1. 通过包裹单号找到格口ID auto it parcelToLockerMap_.find(parcelId); if (it parcelToLockerMap_.end()) { std::cerr 错误未找到包裹 parcelId 的信息。 std::endl; return false; } int lockerId it-second; // 2. 获取格口和订单这里需要根据parcelId找到对应的订单简化处理 // 假设我们通过遍历orders_查找实际应维护parcelId到orderId的映射 ParcelOrder* targetOrder nullptr; for (auto [orderId, order] : orders_) { if (order.getParcelId() parcelId !order.isPickedUp()) { targetOrder order; break; } } if (!targetOrder) { std::cerr 错误包裹订单状态异常或已取件。 std::endl; return false; } // 3. 验证取件码此处简化真实场景应比对哈希值 std::string correctCode getStoredCodeForParcel(parcelId); // 从某个地方获取存储的码 if (inputCode ! correctCode) { std::cerr 错误取件码不正确。 std::endl; return false; } // 4. 执行取件更新格口状态 CabinetLocker locker lockers_[lockerId]; std::string retrievedParcelId locker.retrieveParcel(); // 内部会将状态置为EMPTY if (retrievedParcelId ! parcelId) { // 严重错误数据不一致 std::cerr 严重错误格口数据不一致 std::endl; return false; } // 5. 更新订单状态 targetOrder-markAsPickedUp(); // 6. 清理快速查找映射 parcelToLockerMap_.erase(parcelId); // 7. 保存数据 saveToFile(); std::cout 包裹 parcelId 已从格口 lockerId 成功取出。 std::endl; return true; }安全提醒取件码的验证绝对不能像上面例子那样明文存储和比对在真实系统中取件码或密码应该使用单向哈希算法如SHA-256处理后存储。验证时将用户输入的码进行同样的哈希运算再与存储的哈希值比对。getStoredCodeForParcel函数应该返回的是哈希值。可以使用像OpenSSL或Crypto库或者C11以后的functional里的std::hash但后者通常用于容器密码学上不够安全。3.3 状态查询与维护功能一个完整的管理系统还需要后台管理功能比如查看所有格口状态、查询超时包裹、手动处理异常等。查看所有格口状态这很简单遍历lockers_并格式化输出即可。void LockerManagementSystem::displayAllLockersStatus() const { std::cout 快递柜状态总览 std::endl; std::cout std::left std::setw(8) 格口ID std::setw(10) 尺寸 std::setw(15) 状态 std::setw(20) 包裹单号 std::endl; for (const auto locker : lockers_) { std::cout std::left std::setw(8) locker.getId() std::setw(10) sizeToString(locker.getSize()) std::setw(15) statusToString(locker.getStatus()) std::setw(20) locker.getParcelId() std::endl; } }查找超时包裹这是体现时间计算的地方。我们需要遍历所有状态为OCCUPIED或AWAITING_PICKUP的格口检查其存放时间是否超过阈值。std::vectorint LockerManagementSystem::getOverdueLockers(int thresholdHours) const { std::vectorint overdueIds; auto now std::chrono::system_clock::now(); for (const auto locker : lockers_) { if (locker.isEmpty() || locker.isFaulty()) { continue; } auto storageDuration std::chrono::duration_caststd::chrono::hours( now - locker.getStoreTime()); if (storageDuration.count() thresholdHours) { overdueIds.push_back(locker.getId()); } } return overdueIds; }标记格口故障管理员功能将某个格口状态设置为FAULTY并强制取出其中的包裹如果有。bool LockerManagementSystem::markLockerFaulty(int lockerId, bool forceRetrieve) { if (lockerId 0 || lockerId lockers_.size()) { return false; } CabinetLocker locker lockers_[lockerId]; if (locker.isFaulty()) { return true; // 已经是故障状态 } if (!locker.isEmpty() forceRetrieve) { // 强制取件记录日志清理关联订单和映射 std::string parcelId locker.getParcelId(); std::cout 警告格口 lockerId 被标记为故障强制取出包裹 parcelId std::endl; // ... 清理 orders_ 和 parcelToLockerMap_ 中相关记录 locker.retrieveParcel(); // 清空格口 } else if (!locker.isEmpty()) { std::cerr 错误格口 lockerId 非空无法标记故障。请先取出包裹或使用强制选项。 std::endl; return false; } locker.setStatus(LockerStatus::FAULTY); saveToFile(); return true; }4. 数据持久化的具体实现与文件管理我们选择了JSON作为持久化格式现在来看看saveToFile和initializeFromFile的具体实现。4.1 将系统状态保存到JSON文件我们需要将lockers_和orders_两个核心容器序列化。bool LockerManagementSystem::saveToFile() const { nlohmann::json systemJson; // 序列化格口数组 nlohmann::json lockersArray nlohmann::json::array(); for (const auto locker : lockers_) { nlohmann::json lockerJson; lockerJson[id] locker.getId(); lockerJson[size] static_castint(locker.getSize()); lockerJson[status] static_castint(locker.getStatus()); lockerJson[parcelId] locker.getParcelId(); // 序列化时间 auto storeTime locker.getStoreTime(); if (storeTime ! CabinetLocker::INVALID_TIME()) { // 定义一个无效时间常量 auto time_t_store std::chrono::system_clock::to_time_t(storeTime); std::ostringstream oss; oss std::put_time(std::localtime(time_t_store), %Y-%m-%d %H:%M:%S); lockerJson[storeTime] oss.str(); } else { lockerJson[storeTime] nullptr; } lockersArray.push_back(lockerJson); } systemJson[lockers] lockersArray; // 序列化订单映射 nlohmann::json ordersObject; for (const auto [orderId, order] : orders_) { nlohmann::json orderJson; orderJson[parcelId] order.getParcelId(); orderJson[lockerId] order.getLockerId(); orderJson[courierId] order.getCourierId(); orderJson[receiverPhone] order.getReceiverPhone(); orderJson[storedTime] timePointToString(order.getStoredTime()); orderJson[pickedUpTime] timePointToString(order.getPickedUpTime()); orderJson[isPickedUp] order.isPickedUp(); ordersObject[orderId] orderJson; // 使用orderId作为键 } systemJson[orders] ordersObject; // 写入文件 std::ofstream ofs(lockersDataFile_); if (!ofs.is_open()) { std::cerr 无法打开文件进行写入: lockersDataFile_ std::endl; return false; } ofs systemJson.dump(4); // 参数4表示缩进4个空格美化输出 ofs.close(); return true; }4.2 从JSON文件加载系统状态加载是保存的逆过程需要解析JSON并重建对象。bool LockerManagementSystem::initializeFromFile() { std::ifstream ifs(lockersDataFile_); if (!ifs.is_open()) { std::cerr 数据文件不存在将使用初始空系统。 std::endl; // 可以在这里初始化一些默认格口 initializeDefaultLockers(); return true; // 或 false取决于业务逻辑 } nlohmann::json systemJson; try { ifs systemJson; } catch (nlohmann::json::parse_error e) { std::cerr 解析JSON文件失败: e.what() std::endl; return false; } // 清空现有数据 lockers_.clear(); orders_.clear(); parcelToLockerMap_.clear(); // 反序列化格口 if (systemJson.contains(lockers) systemJson[lockers].is_array()) { for (const auto lockerJson : systemJson[lockers]) { int id lockerJson[id].getint(); LockerSize size static_castLockerSize(lockerJson[size].getint()); CabinetLocker locker(id, size); LockerStatus status static_castLockerStatus(lockerJson[status].getint()); locker.setStatus(status); std::string parcelId lockerJson[parcelId].getstd::string(); if (!parcelId.empty()) { locker.setParcelId(parcelId); } if (lockerJson[storeTime].is_string()) { std::string timeStr lockerJson[storeTime]; auto storeTime stringToTimePoint(timeStr); locker.setStoreTime(storeTime); } lockers_.push_back(locker); } } // 反序列化订单 if (systemJson.contains(orders) systemJson[orders].is_object()) { for (auto [orderId, orderJson] : systemJson[orders].items()) { ParcelOrder order( orderJson[parcelId].getstd::string(), orderJson[lockerId].getint(), orderJson[courierId].getstd::string(), orderJson[receiverPhone].getstd::string() ); order.setStoredTime(stringToTimePoint(orderJson[storedTime].getstd::string())); if (orderJson[isPickedUp].getbool()) { order.markAsPickedUp(); // 这个方法内部会设置pickedUpTime order.setPickedUpTime(stringToTimePoint(orderJson[pickedUpTime].getstd::string())); } orders_[orderId] order; // 重建包裹-格口映射仅限未取件订单 if (!order.isPickedUp()) { parcelToLockerMap_[order.getParcelId()] order.getLockerId(); } } } return true; }关键细节stringToTimePoint和timePointToString是需要自己实现的辅助函数用于在std::chrono::time_point和字符串之间转换。使用std::get_time和std::put_time可以保证格式统一。务必注意时区问题在存储和加载时最好统一使用UTC时间或明确的时区标识。5. 用户界面与控制台交互设计虽然核心是后台逻辑但一个简单的控制台界面对于演示和测试至关重要。我们可以设计一个基于文本菜单的交互循环。class ConsoleUI { private: LockerManagementSystem system_; // 持有系统引用 public: ConsoleUI(LockerManagementSystem sys) : system_(sys) {} void run() { bool running true; while (running) { displayMainMenu(); int choice getMenuChoice(); switch (choice) { case 1: handleStoreParcel(); break; case 2: handlePickupParcel(); break; case 3: system_.displayAllLockersStatus(); break; case 4: handleAdminMenu(); break; // 管理员菜单 case 0: running false; std::cout 系统退出数据已保存。 std::endl; break; default: std::cout 无效选择请重新输入。 std::endl; } std::cout std::endl; } } void displayMainMenu() { std::cout \n 快递柜管理系统 std::endl; std::cout 1. 快递员存件 std::endl; std::cout 2. 收件人取件 std::endl; std::cout 3. 查看所有格口状态 std::endl; std::cout 4. 管理员功能 std::endl; std::cout 0. 退出系统 std::endl; std::cout 请选择操作: ; } void handleStoreParcel() { std::string parcelId, courierId, receiverPhone; int sizeChoice; std::cout \n--- 快递员存件 --- std::endl; std::cout 请输入包裹单号: ; std::cin parcelId; std::cout 请选择包裹尺寸 (1-小, 2-中, 3-大): ; std::cin sizeChoice; LockerSize size static_castLockerSize(sizeChoice - 1); // 假设枚举值从0开始 std::cout 请输入快递员ID: ; std::cin courierId; std::cout 请输入收件人手机号: ; std::cin receiverPhone; if (system_.storeParcel(parcelId, size, courierId, receiverPhone)) { std::cout 存件操作成功 std::endl; } else { std::cout 存件操作失败。 std::endl; } } void handlePickupParcel() { std::string parcelId, verificationCode; std::cout \n--- 收件人取件 --- std::endl; std::cout 请输入包裹单号: ; std::cin parcelId; std::cout 请输入取件码: ; std::cin verificationCode; if (system_.pickupParcel(parcelId, verificationCode)) { std::cout 取件成功 std::endl; } else { std::cout 取件失败请检查信息。 std::endl; } } // ... 其他菜单处理函数 };在主函数中初始化系统并启动UI即可。int main() { // 初始化管理系统指定数据文件路径 LockerManagementSystem system(lockers_data.json, orders_data.json); if (!system.initializeFromFile()) { std::cerr 系统初始化失败退出。 std::endl; return 1; } ConsoleUI ui(system); ui.run(); // 进入主循环 // 退出前system的析构函数或run()的退出逻辑应确保数据已保存 return 0; }6. 项目扩展方向与性能优化思考实现基础版本后这个项目还有很大的扩展空间可以让你练习更高级的C特性。1. 引入智能指针管理资源目前我们使用原始指针和STL容器管理对象。可以考虑在LockerManagementSystem中使用std::unique_ptrCabinetLocker的向量来管理格口明确所有权关系。订单映射也可以使用std::unique_ptrParcelOrder。2. 实现多线程安全如果系统需要处理并发请求比如模拟多个终端同时操作那么对共享数据lockers_,orders_的访问就需要加锁。可以使用std::mutex和std::lock_guard来保护关键区域例如storeParcel和pickupParcel方法。class ThreadSafeLockerSystem : public LockerManagementSystem { private: mutable std::mutex dataMutex_; // 可变的因为const方法也可能需要锁 public: bool threadSafeStoreParcel(...) { std::lock_guardstd::mutex lock(dataMutex_); return storeParcel(...); // 调用父类方法 } // ... 其他需要线程安全的方法 };3. 使用数据库替代文件存储集成SQLite数据库。你可以创建lockers和orders表使用SQLite C/C接口或像SQLiteCpp这样的C封装库来执行SQL语句。这将让你练习数据库操作和更复杂的数据关系管理。4. 添加计费模块根据包裹大小、存放时长实现计费逻辑。这需要扩展ParcelOrder类增加费用字段和计算逻辑并在取件或查询时显示。5. 构建图形化界面使用Qt框架为系统创建一个桌面GUI。你可以用QTableWidget展示格口状态用按钮和输入框实现存件取件操作。这将项目从一个控制台程序升级为一个真正的桌面应用。关于性能对于单机、数据量不大的场景当前的实现已经足够。如果格口数量巨大比如上万线性查找可用格口findAvailableLocker可能成为瓶颈。此时可以考虑维护一个按尺寸分类的“空闲格口索引”例如使用std::mapLockerSize, std::setint其中set存储该尺寸下所有空闲格口的ID。分配时直接从对应尺寸的set中取第一个分配和回收时更新这个索引结构可以将查找复杂度降到O(log n)。7. 常见编译问题与调试技巧在实现这个项目的过程中尤其是使用较新的C标准如C11/14/17时你可能会遇到一些编译和链接问题。1. JSON库的集成如果你使用nlohmann/json最简单的方法是下载单头的json.hpp文件放到你的项目目录中然后在源文件里#include json.hpp即可。确保你的编译器支持C11或更高版本。在CMakeLists.txt或编译命令中不需要特别的链接参数。2. 时间库的跨平台问题chrono库是C11标准的一部分现代编译器都支持。但将time_point转换为字符串时std::put_time和std::get_time在GCC/MinGW上可能需要包含iomanip头文件并且注意MinGW早期版本可能实现不完整。如果遇到问题可以考虑使用strftime和strptimePOSIX函数Windows下可用strftime_s和自定义解析作为备选。3. 枚举类的序列化我们通过static_castint将枚举转换为整数存储。反序列化时再转换回来。务必确保枚举的底层整数值是稳定、明确的。你也可以选择存储枚举值的字符串名称这样可读性更好但代码稍复杂。4. 调试数据结构当存、取件逻辑出现bug时最有效的调试方法是 *打印关键状态在storeParcel和pickupParcel的关键步骤后打印lockers_和orders_的相关内容。 *检查JSON文件直接打开生成的JSON数据文件查看对象状态是否正确序列化。 *使用断言在代码中合理使用assert例如在parcelToLockerMap_中查找包裹时可以断言找到的格口ID是有效的。5. 内存管理本项目主要使用STL容器vector,unordered_map它们会自动管理内存。唯一需要注意的是如果未来在类中使用了原始指针指向动态分配的内存务必遵循RAII原则在析构函数中正确释放或者直接使用智能指针。实现这样一个系统最大的收获不是写出了多少行代码而是学会了如何将一个模糊的现实需求逐步分解为清晰的对象、精确的状态和严谨的交互流程。从枚举定义到类关系从内存管理到数据持久化每一步都迫使你去思考C特性如何为业务逻辑服务。当你看到控制台里成功完成一次存件、取件数据在文件里完好保存时那种对代码掌控感的提升是单纯看书和做练习题无法比拟的。

相关新闻