如何在C++中构建合理类层次?Course与Lecture类设计咨询
问题解答
类关系判断:你的思路完全正确
Course和Lecture是组合关系——一门课程包含多场讲座,完全没必要用继承。继承是用来表达"is-a"的关系(比如MathLecture是一种Lecture),但讲座显然不是课程的一种,所以Course类持有Lecture集合的设计非常合理。
现有代码的几个问题
Lecture类定义在Course之后,编译器无法识别Course中的Lecture* m_lectures,需要先提前声明Lecture类;- 使用裸指针
Lecture*管理数组极易引发内存泄漏,建议用std::vector<Lecture>这类标准容器替代,自动管理内存更省心; - 所有成员都设为私有,且没有对外的构造函数、访问方法,根本无法创建对象、添加讲座,更没法实现要求的三个功能;
- 缺少数组/容器的长度信息,连遍历讲座都做不到。
改进后的完整代码实现
#include <string> #include <vector> #include <algorithm> #include <optional> #include <iostream> // 提前声明Lecture类,解决编译器识别问题 class Lecture; class Course { private: std::string m_name; std::string m_teacher_surname; std::vector<Lecture> m_lectures; // 用vector替代裸指针,动态管理讲座 public: // 构造函数:初始化课程名称和教师姓氏 Course(std::string name, std::string teacher_surname) : m_name(std::move(name)), m_teacher_surname(std::move(teacher_surname)) {} // 添加讲座的方法 void add_lecture(Lecture lecture) { m_lectures.push_back(std::move(lecture)); } // 1. 找出学生最少的讲座(返回optional处理无讲座的边界情况) std::optional<const Lecture*> find_least_attended_lecture() const { if (m_lectures.empty()) return std::nullopt; const auto& min_lecture = *std::min_element(m_lectures.begin(), m_lectures.end(), [](const Lecture& a, const Lecture& b) { return a.get_present_students() < b.get_present_students(); }); return &min_lecture; } // 2. 获取包含特定关键词的主题列表 std::vector<std::string> get_topics_with_keyword(const std::string& keyword) const { std::vector<std::string> matched_topics; for (const auto& lecture : m_lectures) { const std::string& topic = lecture.get_topic(); if (topic.find(keyword) != std::string::npos) { matched_topics.push_back(topic); } } return matched_topics; } // 3. 获取教师姓氏的最后一个字符 char get_last_char_of_teacher_surname() const { if (m_teacher_surname.empty()) return '\0'; // 处理空姓氏的异常情况 return m_teacher_surname.back(); } // 辅助打印课程信息,方便演示 void print_course_info() const { std::cout << "课程名称: " << m_name << "\n"; std::cout << "教师姓氏: " << m_teacher_surname << "\n"; std::cout << "讲座总数: " << m_lectures.size() << "\n"; } }; class Lecture { private: std::string m_date; std::string m_topic; int m_present_students; public: // 构造函数:初始化讲座的日期、主题、到场学生数 Lecture(std::string date, std::string topic, int present_students) : m_date(std::move(date)), m_topic(std::move(topic)), m_present_students(present_students) {} // 私有成员的访问器方法(保证封装性的同时提供必要的访问接口) int get_present_students() const { return m_present_students; } const std::string& get_topic() const { return m_topic; } const std::string& get_date() const { return m_date; } }; // 功能演示 int main() { Course cpp_course("C++进阶编程", "Smith"); cpp_course.add_lecture(Lecture("2024-05-01", "智能指针原理与实践", 35)); cpp_course.add_lecture(Lecture("2024-05-08", "STL容器深度解析", 28)); cpp_course.add_lecture(Lecture("2024-05-15", "Lambda表达式与算法应用", 32)); cpp_course.print_course_info(); // 查找学生最少的讲座 if (auto least_lecture = cpp_course.find_least_attended_lecture()) { std::cout << "\n学生最少的讲座: " << (*least_lecture)->get_topic() << ",到场人数: " << (*least_lecture)->get_present_students() << "\n"; } // 查找含"容器"关键词的主题 auto matched_topics = cpp_course.get_topics_with_keyword("容器"); std::cout << "\n含'容器'的主题列表:\n"; for (const auto& topic : matched_topics) { std::cout << "- " << topic << "\n"; } // 获取教师姓氏最后一个字符 std::cout << "\n教师姓氏最后一个字符: " << cpp_course.get_last_char_of_teacher_surname() << "\n"; return 0; }
代码说明
- 用
std::vector<Lecture>替代裸指针,既避免内存泄漏,又支持动态添加讲座; - 为两个类添加了构造函数和必要的访问器,在保证封装性的前提下,提供了操作接口;
- 完整实现了要求的三个功能:
- 用
std::min_element结合Lambda表达式快速找到学生最少的讲座,用std::optional处理无讲座的边界情况; - 遍历所有讲座,通过
std::string::find匹配关键词,收集符合条件的主题; - 用
std::string::back()直接获取姓氏最后一个字符,同时处理了空字符串的异常情况;
- 用
- 附带
main函数演示了所有功能的使用方式。
内容的提问来源于stack exchange,提问作者Anakin Codewalker
相关产品推荐
相关产品推荐

