大学生校园管理体系 - 项目总结

[复制链接]
发表于 2026-8-14 09:19:51 | 显示全部楼层 |阅读模式
大学生校园管理体系 - 项目总结

大家好,我是一名C++初学者。花了几周时间,我从零写了一个大学生校园管理体系。项目采用C++和SQLite实现,支持三种角色:管理员、教师、学生,覆盖了教务管理的核心业务流程——用户管理、课程发布、选课退课、成绩录入、数据导出等。
这个项目虽然不大,但麻雀虽小五脏俱全。下面我把开发过程中的收获和踩过的坑分享出来,希望对同样在学习C++的朋友有帮助。
一、项目到底是干啥的?

简单说,这就是一个给大学用的管理体系,三个角色各干各的事:
管理员:管学院、管专业、管用户(增删改查)、导出所有数据
教师:发布课程、查看选课学生、录入成绩、导出学生名单
学生:浏览课程、选课退课、查看课表、查GPA
说白了就是模拟了一个真实的大学教务场景,从用户登录到各种业务操纵,流程是完整的。
二、项目用到了哪些技术?

技术        用途
C++        核心编程语言
SQLite        轻量级数据库,不需要单独安装服务器
面向对象编程        类、继续、多态、封装
筹划模式        单例、工厂、战略
STL        vector、map、string、unique_ptr
SQLite是真的方便,一个 university.db 文件就搞定了,不用像MySQL那样还要配置服务器环境,对初学者很友好。
三、代码整体结构

项目目次/
├── include/          # 头文件(所有.h)
│   ├── User.h        # 用户基类
│   ├── AdminUser.h   # 管理员类
│   ├── TeacherUser.h # 教师类
│   ├── StudentUser.h # 学生类
│   ├── DatabaseManager.h # 数据库管理
│   ├── CourseManager.h   # 课程管理
│   └── ...
├── src/              # 源文件(所有.cpp)
│   ├── Main.cpp      # 程序入口
│   ├── DatabaseManager.cpp
│   ├── UserManager.cpp
│   └── ...
├── schema.sql        # 建表脚本
├── university.db     # SQLite数据库文件(运行后天生)
├── exports/          # 导出CSV文件存放目次
└── Makefile          # 编译脚本
这实在就是分层筹划的思绪:
include/ 放接口声明(.h)
src/ 放具体实现(.cpp)
业务逻辑和数据库操纵分开
四、登录和角色识别 - 多态的核心表现


登录流程是这样的:
// Main.cpp
auto user = UserManager::authenticate(username, password);
if (user) {
AccessControl::setCurrentUser(user.get());
user->showMenu();  // 这里调用的是虚函数!
}
重点是 user->showMenu() 这一行。
User 基类里 showMenu() 是纯虚函数:
virtual void showMenu() = 0;  // 没有实现,子类必须重写
子类各自实现了自己的菜单:
子类        showMenu()显示的内容
AdminUser        管理学院、管理专业、管理用户、导出数据
TeacherUser        查看课程、发布课程、录入成绩、导出名单
StudentUser        浏览课程、选课退课、查看课表、查GPA


登录成功后,程序根本不知道具体是哪个子类,但调用 showMenu() 时,C++会根据实际对象类型自动调用对应的实现——这就是运行时多态。
五、三个筹划模式的实际应用

1. 单例模式 - DatabaseManager

整个程序只需要一个数据库连接,所以我把 DatabaseManager 筹划成了单例:
class DatabaseManager {
private:
static DatabaseManager* instance;  // 唯一的实例指针
DatabaseManager() {}               // 构造函数私有,外部不能new
public:
static DatabaseManager* getInstance() {
if (!instance) {
instance = new DatabaseManager();
}
return instance;
}
};
任何地方要用数据库,都是:
DatabaseManager* db = DatabaseManager::getInstance();
db->querySQL("SELECT ...");
为什么这么筹划?
省资源:一个连接就够了,不用每次操纵都打开关闭
统一管理:所有数据库操纵都走同一个对象,好维护
2. 工厂模式 - UserFactory

登录成功后,要根据角色创建不同的用户对象。这里用了工厂模式:
// UserFactory.h
class UserFactory {
public:
static unique_ptr createUser(const string& role, ...) {
if (role == "admin") {
return make_unique(...);
} else if (role == "teacher") {
return TeacherUser::loadTeacherInfo(userId);
} else if (role == "student") {
return StudentUser::loadStudentInfo(userId);
}
return nullptr;
}
};
好处是啥?
调用方只需要说"给我创建一个用户",不用关心具体是创建 AdminUser 还是 StudentUser。创建逻辑集中在一个地方,以后新增角色只用改工厂类,不用改调用代码。
3. 战略模式 - 选课战略

选课的时候,不同场景可能需要不同的规则:
class SelectionStrategy {
public:
virtual bool canSelect(int studentId, int courseId) = 0;
};
// 先到先得 - 检查容量
class FirstComeFirstServedStrategy : public SelectionStrategy {
bool canSelect(...) {
// 检查已选人数是否小于容量
}
};
// 时间冲突检测 - 检查是否和已选课程时间冲突
class TimeConflictStrategy : public SelectionStrategy {
bool canSelect(...) {
// 检查时间段是否重叠
}
};
// 组合战略 - 多个条件一起检查
class CompositeStrategy : public SelectionStrategy {
// 组合多个战略一起实行
};
用的时候:
auto strategy = make_unique();
CourseManager :: setSelectionStrategy(move(strategy));
战略模式的好处:把"选课规则"从"选课逻辑"中抽出来,以后规则变了(比如改成"绩点高的优先"),只需要新增一个战略类,不用改核心逻辑。
六、数据库操纵 - 封装SQLite

我用SQLite的C接口,封装了一个 DatabaseManager 类:
// 查询 - 返回vector
auto results = db->querySQL("SELECT * FROM users");
for (const auto& row : results) {
cout beginTransaction();
// 实行多个DELETE
if (所有成功) {
db->commitTransaction();
} else {
db->rollbackTransaction();
}
3. 编码转换(GBK ↔ UTF-8)

因为Windows控制台默认是GBK编码,而SQLite存的是UTF-8,所以在读写中文时需要转换:
string utf8Str = db->gb2312ToUtf8(gbkStr);  // 输入时转UTF-8存
string gbkStr = db->utf8ToGb2312(utf8Str);  // 读取时转GBK显示
这个坑折腾了我好久,一开始中文全是乱码。
4. SQL注入防范

简单做了转义:
string escapeString(const string& str) {
// 把单引号变成两个单引号
// 把反斜杠变成两个反斜杠
}
但说实话,更好的做法是用SQLite的参数化查询(sqlite3_bind),这个是我后续要改进的地方。
七、STL容器的使用

vector - 动态数组
存列表数据,比如所有课程、所有用户:
vector courses = CourseManager::getAllCourses();
vector users = UserManager::getAllUsers();
map - 键值对
存数据库查询结果,每行数据就是一个 map,字段名→字段值:
vector results = db->querySQL(sql);
for (const auto& row : results) {
string name = row.at("name");  // 通过字段名取值
}
unique_ptr - 独占式智能指针
管理用户对象,不用手动delete:
unique_ptr user = UserManager::authenticate(username, password);
// user离开作用域自动释放
八、面向对象基础知识点总结

1. 什么是多态?代码哪里用到了?

多态就是同一个接口,不同的实现。
我的代码里,User 基类的 showMenu() 是虚函数,AdminUser、TeacherUser、StudentUser 各自重写。登录后调用 user->showMenu(),会自动实行对应角色的菜单,这就是运行时多态。
2.虚函数和纯虚函数的区别?

虚函数纯虚函数写法virtual void func() {}virtual void func() = 0;有默认实现✅ 有❌ 没有子类必须重写❌ 可选✅ 必须类能否实例化✅ 能❌ 不能(抽象类)我的 User 类中 showMenu() = 0 是纯虚函数,所以 User 是抽象类,不能直接创建对象。
3. 为什么要用虚析构函数?

如果不把析构函数设为虚函数,delete 基类指针时只会调用基类的析构函数,派生类的资源就释放不掉了。
User* user = new StudentUser(...);
delete user;  // 如果User()不是虚函数,StudentUser()不会被调用
所以我在 User 类里加了 virtual ~User() {}。
4. this指针是啥?

this 指向当前对象自己,在非静态成员函数里可以用:
void User::setUsername(const string& name) {
this->username = name;  // this指向当前对象
}
静态函数里不能用 this,因为静态函数属于类不属于对象。
5. RAII是什么?

Resource Acquisition Is Initialization,用对象管理资源。构造函数里获取资源,析构函数里释放资源。
DatabaseManager 在构造函数里打开数据库连接,析构函数里自动关闭,这就是RAII的应用。
6. auto关键字的作用?

自动类型推导,让代码更简洁:
auto user = UserManager::authenticate(username, password);
// 编译器自动推导为 unique_ptr
但不要过分使用,类型明显的时候用auto没问题,类型复杂的时候auto能救命。
九、项目亮点

1.三种筹划模式的实际应用 - 单例、工厂、战略,不是硬套,是确实办理了实际问题
2.分层筹划 - 头文件和实现分离,业务逻辑和数据操纵分离
3.多态实现角色识别 - 登录后自动进入对应菜单,扩展性好
4.SQL和代码分离 - schema.sql独立存放,改表结构不用重新编译
5.事件保证数据一致性 - 删除学院时级联删除用事件掩护
6.CSV导出 - 所有表都能导出,方便查看和备份
7.自动创建管理员 - 首次运行自动建表并天生 admin/admin123
十、值得总结的技术点

1. SQLite的C接口封装

// 打开连接
sqlite3_open(path.c_str(), &db);
// 实行SQL(无返回)
sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg);
// 查询(有返回)
sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 处理每一行
}
sqlite3_finalize(stmt);
2. 文件读写


// 读文件
ifstream file(filename);
stringstream buffer;
buffer

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表