结对编程实验:简易在线考试系统

一、实验基本信息

• 实验名称:第7周 结对编程02 简易在线考试系统开发

• 开发语言:C++

• 小组成员:学号2452821、学号2452822

• 完成时间:2026年4月21日

二、需求分析

  1. 三大核心功能

◦ 题目管理模块

◦ 考生限时答题模块

◦ 考试结束自动判分模块

  1. 附加扩展功能

◦ 题目管理:支持单选、多选、判断题的增加、删除、修改、查询

◦ 答题功能:限时倒计时考试、实时保存考生作答答案

◦ 判分与报告:考完立即自动计算总分、正确率,生成带错题详细解析的个人成绩报告

◦ 自由拓展:答案自动格式化校验、基础示例题库预置、操作容错提示

三、整体设计思路

  1. 数据结构设计

结构体/枚举及其作用:

QuestionType枚举:用于区分题目类型:单选(SINGLE)、多选(MULTIPLE)、判断(JUDGMENT);

Question结构体:用于存储单题完整数据:题目ID、类型、题干、选项、标准答案、题目解析;

UserAnswer结构体:用于记录考生答题记录:对应题目ID、考生答案、是否作答正确;

全局vector:用作全局题库容器,持久存储所有题目数据;

  1. 功能模块拆分

  2. 工具辅助函数

◦ trim():自动去除输入空格、统一答案大小写,避免格式输入错误导致误判

  1. 题目管理子模块

◦ addQuestion():新增题目,自动校验ID不重复

◦ deleteQuestion():根据ID删除指定题目

◦ modifyQuestion():修改已有题目题干、选项、答案、解析

◦ queryQuestion():根据ID查询并展示完整题目详情

◦ manageMenu():题目管理功能菜单页面

  1. 考试答题子模块

◦ takeExam():限时考试流程,实时刷新剩余考试时间

◦ 逐题展示题干选项、实时记录考生作答

◦ 超时自动强制交卷

  1. 自动判分与报告模块

◦ 考试结束批量比对考生答案与标准答案

◦ 自动统计总分、得分率

◦ 汇总所有错题,逐条展示用户错误作答、正确答案、官方解析

  1. 主程序入口

◦ 预置3道基础示例测试题目

◦ 系统主菜单,支持切换题目管理、开始考试、退出系统

  1. 核心流程逻辑

  2. 程序启动 → 加载预置测试题目

  3. 用户选择功能:题目管理 / 开始考试 / 退出

  4. 题目管理页:完成题目的增删改查操作

  5. 开始考试:输入限时时长 → 逐题作答、实时倒计时

  6. 考试结束/时间截止 → 自动判分 → 输出成绩+错题解析报告

  7. 返回系统主菜单

四、完整程序代码

include

include

include

include

include

include

using namespace std;

// 题目类型枚举
enum QuestionType { SINGLE, MULTIPLE, JUDGMENT };

// 题目结构体
struct Question {
int id;
QuestionType type;
string title;
vector options;
string answer;
string analysis;
};

// 用户答题记录结构体
struct UserAnswer {
int qid;
string userAns;
bool isCorrect;
};

// 全局题库
vector questions;

// 字符串格式化:去空格、转大写
string trim(const string &s) {
string res;
for (int i = 0; i < s.size(); ++i) {
char c = s[i];
if (!isspace(c))
res += toupper(c);
}
return res;
}

// 新增题目
void addQuestion() {
Question q;
cout << "请输入题目编号:";
cin >> q.id;

bool exist = false;
for (int i = 0; i < questions.size(); ++i) {if (questions[i].id == q.id) {exist = true;break;}
}
if (exist) {cout << "编号已存在!\n";return;
}int type;
cout << "题目类型 (1单选 2多选 3判断):";
cin >> type;
q.type = (type == 1 ? SINGLE : (type == 2 ? MULTIPLE : JUDGMENT));
cin.ignore();cout << "题干:";
getline(cin, q.title);// 判断题固定选项
if (q.type == JUDGMENT) {q.options.push_back("A. 对");q.options.push_back("B. 错");
} else {for (int i = 0; i < 4; ++i) {string opt;cout << "选项" << (char)('A' + i) << ":";getline(cin, opt);q.options.push_back(string(1, 'A' + i) + ". " + opt);}
}cout << "正确答案(如A、AC):";
getline(cin, q.answer);
q.answer = trim(q.answer);cout << "解析:";
getline(cin, q.analysis);questions.push_back(q);
cout << "添加成功!\n";

}

// 删除题目
void deleteQuestion() {
int id;
cout << "请输入要删除的题目编号:";
cin >> id;
vector newQs;
for (int i = 0; i < questions.size(); ++i) {
if (questions[i].id != id)
newQs.push_back(questions[i]);
}
if (newQs.size() != questions.size()) {
questions = newQs;
cout << "删除成功!\n";
} else {
cout << "题目不存在!\n";
}
}

// 修改题目
void modifyQuestion() {
int id;
cout << "请输入要修改的题目编号:";
cin >> id;
int idx = -1;
for (int i = 0; i < questions.size(); ++i) {
if (questions[i].id == id) {
idx = i;
break;
}
}
if (idx == -1) {
cout << "题目不存在!\n";
return;
}
cin.ignore();

cout << "新题干(原:" << questions[idx].title << "):";
getline(cin, questions[idx].title);if (questions[idx].type != JUDGMENT) {for (int i = 0; i < questions[idx].options.size(); ++i) {cout << "新选项" << (char)('A' + i) << "(原:" << questions[idx].options[i] << "):";getline(cin, questions[idx].options[i]);}
}cout << "新正确答案(原:" << questions[idx].answer << "):";
string ans;
getline(cin, ans);
questions[idx].answer = trim(ans);cout << "新解析(原:" << questions[idx].analysis << "):";
getline(cin, questions[idx].analysis);
cout << "修改成功!\n";

}

// 查询题目
void queryQuestion() {
int id;
cout << "请输入要查询的题目编号:";
cin >> id;
int idx = -1;
for (int i = 0; i < questions.size(); ++i) {
if (questions[i].id == id) {
idx = i;
break;
}
}
if (idx == -1) {
cout << "题目不存在!\n";
return;
}
cout << "\n题干:" << questions[idx].title << "\n";
for (int i = 0; i < questions[idx].options.size(); ++i) {
cout << questions[idx].options[i] << "\n";
}
cout << "正确答案:" << questions[idx].answer << "\n解析:" << questions[idx].analysis << "\n";
}

// 题目管理菜单
void manageMenu() {
while (true) {
cout << "\n===== 题目管理 =====\n";
cout << "1. 添加题目 2. 删除题目 3. 修改题目 4. 查询题目 5. 返回\n";
int op;
cin >> op;
if (op == 5) break;
switch(op) {
case 1: addQuestion(); break;
case 2: deleteQuestion(); break;
case 3: modifyQuestion(); break;
case 4: queryQuestion(); break;
default: cout << "无效选项!\n";
}
}
}

// 开始考试答题
void takeExam() {
if (questions.empty()) {
cout << "题库为空,请先添加题目!\n";
return;
}

int timeLimit;
cout << "请输入考试时间(分钟):";
cin >> timeLimit;
time_t start = time(NULL);
time_t endTime = start + timeLimit * 60;vector<UserAnswer> userAnswers;
int score = 0;for (int i = 0; i < questions.size(); ++i) {Question q = questions[i];time_t now = time(NULL);if (now >= endTime) {cout << "\n考试时间到!自动交卷。\n";break;}int leftSec = (int)(endTime - now);cout << "\n剩余时间:" << leftSec / 60 << "分" << leftSec % 60 << "秒\n";cout << "题号" << q.id << ":" << q.title << "\n";for (int j = 0; j < q.options.size(); ++j) {cout << q.options[j] << "\n";}string ans;cout << "你的答案:";cin >> ans;ans = trim(ans);bool correct = (ans == q.answer);if (correct) score += 10;UserAnswer ua;ua.qid = q.id;ua.userAns = ans;ua.isCorrect = correct;userAnswers.push_back(ua);
}// 成绩报告输出
cout << "\n===== 考试结束 =====\n";
int totalScore = questions.size() * 10;
cout << "总分:" << score << "/" << totalScore << "\n";
cout << "正确率:" << score * 100.0 / totalScore << "%\n";cout << "\n错题解析:\n";
for (int i = 0; i < userAnswers.size(); ++i) {UserAnswer ua = userAnswers[i];if (!ua.isCorrect) {int qidx = -1;for (int j = 0; j < questions.size(); ++j) {if (questions[j].id == ua.qid) {qidx = j;break;}}if (qidx != -1) {cout << "题号" << ua.qid << ":\n";cout << "你的答案:" << ua.userAns << " 正确答案:" << questions[qidx].answer << "\n";cout << "解析:" << questions[qidx].analysis << "\n\n";}}
}

}

// 主函数与系统入口
int main() {
// 预置3道示例题目
Question q1;
q1.id = 1;
q1.type = SINGLE;
q1.title = "C++中哪个是输出语句?";
q1.options.push_back("A. printf");
q1.options.push_back("B. cout");
q1.options.push_back("C. scanf");
q1.options.push_back("D. cin");
q1.answer = "B";
q1.analysis = "cout是C++标准输出流对象";
questions.push_back(q1);

Question q2;
q2.id = 2;
q2.type = JUDGMENT;
q2.title = "C++中int类型占4字节。";
q2.options.push_back("A. 对");
q2.options.push_back("B. 错");
q2.answer = "A";
q2.analysis = "标准环境下int占4字节";
questions.push_back(q2);Question q3;
q3.id = 3;
q3.type = MULTIPLE;
q3.title = "以下哪些是C++的关键字?";
q3.options.push_back("A. if");
q3.options.push_back("B. for");
q3.options.push_back("C. then");
q3.options.push_back("D. while");
q3.answer = "ABD";
q3.analysis = "then不是C++关键字";
questions.push_back(q3);// 系统主循环
while (true) {cout << "\n===== 简易在线考试系统 =====\n";cout << "1. 题目管理 2. 开始考试 3. 退出\n";int op;cin >> op;switch(op) {case 1: manageMenu(); break;case 2: takeExam(); break;case 3: cout << "退出系统\n"; return 0;default: cout << "无效选项!\n";}
}
return 0;

}
五、程序运行效果截图

  1. 题目查询功能运行演示

0f90fb2f1aacd801acbcda8229ecac64

  1. 限时考试答题功能运行演示

01307afcc3c6ba08a9a4af88f162fd3c

六、结对编程过程与心得体会

结对分工与流程

本次结对编程严格按照实验要求流程完成:

  1. 前期方案讨论:两人共同梳理需求、拆分功能模块、敲定结构体设计、确认整体代码架构;

  2. 第一轮角色分工:本人负责编写基础框架、题目管理模块;搭档全程旁听、实时审核代码、指出逻辑漏洞与输入bug;

  3. 第二轮交换角色:搭档完善考试计时、自动判分、错题报告模块;本人进行代码复查与优化;

  4. 共同联调:两人一起修复cin和getline混用导致的换行残留bug、完善答案大小写容错、优化用户交互提示;

  5. 最终整体测试、验证全部功能正常运行。

实验收获与体会

  1. 结对编程优势:双人协作极大减少了单人编码的粗心bug,审核角色可以跳出编码细节,更快发现逻辑缺陷,代码质量和开发效率都明显提升;

  2. 技术能力提升:熟练掌握了C++结构体、vector容器、时间函数的综合使用;理解了限时程序的计时逻辑、字符串格式化校验、批量数据比对判分的实现思路;

  3. 工程思维成长:明白了小型项目模块化拆分的重要性,每个功能单独封装函数,代码可读性、可维护性大幅提高;

  4. 不足与优化方向:本次为控制台简易版本,后续可以拓展为文件持久化保存题库、增加用户登录权限、打乱题目顺序、保存历史考试记录等进阶功能。

注:本次实验搭档学号:2452822(全程参与本次结对开发与代码审核工作)