1. 作业背景与核心目标
CS106L作为斯坦福大学计算机科学系的经典课程,其Assignment 1:Simple Enroll项目是学生接触C++实际开发的第一道门槛。这个作业模拟了大学选课系统的核心功能,要求实现课程添加、学生注册、冲突检测等基础操作。不同于纯语法练习,它需要学生综合运用控制流、数据结构、异常处理等知识解决实际问题。
我在担任课程助教期间,发现约65%的学生会在这个作业遇到典型问题:数据结构选择不当导致性能瓶颈、边界条件处理不完善引发内存错误、面向对象设计混乱增加代码维护难度。这个看似简单的作业实际上暗藏玄机,是检验学生能否将课堂知识转化为工程能力的试金石。
2. 系统设计与数据结构选型
2.1 核心类结构设计
典型实现包含三个核心类:
- Course:存储课程编号、名称、时间安排等元数据
- Student:记录学生ID、已选课程列表及时间冲突状态
- EnrollmentSystem:作为中枢管理系统,处理所有注册逻辑
cpp复制class Course {
private:
std::string courseCode;
std::vector<TimeSlot> schedule;
std::unordered_set<std::string> enrolledStudents;
};
关键点:使用unordered_set存储选课学生而非vector,使得查询操作时间复杂度从O(n)降至O(1)
2.2 时间冲突检测算法
课程时间通常表示为TimeSlot结构体:
cpp复制struct TimeSlot {
Weekday day; // 枚举类型:MON, TUE等
int startHour;
int duration; // 课程时长(小时)
};
冲突检测的核心是比较两个TimeSlot是否重叠:
cpp复制bool isOverlap(const TimeSlot& a, const TimeSlot& b) {
if (a.day != b.day) return false;
int aEnd = a.startHour + a.duration;
int bEnd = b.startHour + b.duration;
return a.startHour < bEnd && b.startHour < aEnd;
}
3. 实现过程中的关键难点
3.1 大规模数据下的性能优化
当测试数据量达到10万级课程注册时,线性查找会成为性能瓶颈。我们采用两级索引策略:
- 使用unordered_map建立课程ID到Course对象的O(1)访问
- 为每个学生维护按时间排序的课程列表,使冲突检测可采用二分查找
cpp复制class EnrollmentSystem {
private:
std::unordered_map<std::string, Course> courseDatabase;
std::unordered_map<std::string, Student> studentDatabase;
};
3.2 异常处理最佳实践
常见异常场景包括:
- 重复选课(同一课程多次注册)
- 时间冲突(学生课表已有相同时段课程)
- 课程不存在(输入无效课程编号)
推荐使用C++异常机制:
cpp复制void enrollStudent(const std::string& studentID, const std::string& courseCode) {
if (!courseDatabase.count(courseCode)) {
throw std::runtime_error("Course not found: " + courseCode);
}
// ...其他检查逻辑
}
4. 测试与调试技巧
4.1 单元测试框架搭建
使用Catch2框架构建测试用例:
cpp复制TEST_CASE("Time conflict detection") {
TimeSlot monday9am{Weekday::MON, 9, 1};
TimeSlot monday9_30am{Weekday::MON, 9, 2};
REQUIRE(isOverlap(monday9am, monday9_30am) == true);
}
4.2 内存问题排查
Valgrind工具链的使用示例:
bash复制valgrind --leak-check=full ./enroll_system < test_input.txt
常见内存错误包括:
- 未释放动态分配的Course对象
- 迭代器失效导致vector访问越界
- 浅拷贝引发的重复释放问题
5. 高级功能扩展思路
5.1 等待列表实现
当课程人数达到上限时,可引入等待队列:
cpp复制class Course {
// ...
std::queue<std::string> waitlist;
void addToWaitlist(const std::string& studentID) {
waitlist.push(studentID);
}
};
5.2 课程推荐系统
基于学生已选课程推荐相关课程:
cpp复制std::vector<std::string> recommendCourses(const Student& s) {
// 提取学生已选课程的关键词
// 计算与其他课程的相似度
// 返回Top-N推荐列表
}
6. 提交前的终极检查清单
-
代码规范性
- 类成员变量使用m_前缀或后缀_
- 函数参数采用有意义的命名(避免单纯使用x,y)
- 每行代码不超过80字符
-
功能完整性
- 支持从文件批量导入课程数据
- 正确处理所有边界条件(空输入、极端值等)
- 实现要求的命令行交互接口
-
性能基准
- 万级数据加载时间<1秒
- 注册操作平均响应时间<50ms
- 内存占用稳定无泄漏
这个作业看似简单,但想拿到满分需要处理好诸多细节。我在批改作业时最看重的不是功能实现本身,而是代码中展现出的工程思维——如何选择数据结构、处理异常情况、保证代码可扩展性。建议在基础功能完成后,花时间重构代码结构,这往往比添加新功能更能提升代码质量。