课程设计题目及内容
创新互联公司于2013年创立,是专业互联网技术服务公司,拥有项目成都做网站、网站设计、外贸营销网站建设网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元贵德做网站,已为上家服务,为贵德各地企业和个人服务,联系电话:18982081108题目:学生成绩管理系统
设计要求及提示如下:
(1)、设计一个学生类Student,包括数据成员:姓名、学号、二门课程(面向对象程序设计、高等数学)的成绩。
(2)、创建一个管理学生的类Management,包括实现学生的数据的增加、删除、修改、按课程成绩排序、保存学生数据到文件及加载文件中的数据等功能。
(3)、创建一个基于对话框的MFC应用程序,程序窗口的标题上有你姓名、学号和应用程序名称。使用(1)和(2)中的类,实现对学生信息和成绩的输入和管理。
(4)、创建一个单文档的MFC应用程序,读取(3)中保存的文件中的学生成绩,分别用直方图和折线方式显示所有学生某课程的成绩分布图。
1.观察者模式:一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。MFC中的文档/视图结构其实是观察者模式的一种变体。文档包含数据对象,充当目标角色;用户通过视图更新文档,视图在这里充当观察者角色。一个文档可以有多个视图。一旦用户通过某个视图改变了文档的数据对象,该视图将通过调用UpdateAllViews方法来更新文档。这将触发OnUpdate方法,从而通知其他与文档关联的视图更新显示。视图类通过重载OnUpdate方法从文档对象获取更新数据。
2.模板方法模式:基类用一些抽象的操作定义了一个算法的骨架,子类重定义算法中的特定部分,以完成特定于子类的各种操作。类CView(AbstractClass)里面的函数OnPaint、OnDraw。用户只需在CView的派生类中的OnDraw函数中编写代码就可实现数据的正确显示,而不必关心OnDraw如何被调用。
3.职责链模式:多个对象组成一个对象链,客户请求沿着对象链进行传播,直到有一个对象处理它。在MFC中,所有派生自CCmdTarget的类都能够响应命令消息(WM_COMMAND),所有派生自CWnd的类都能够响应标准Windows消息(除 WM_COMMAND 之外的消息)。因 CWnd派生于CCmdTarget类,故派生自CWnd的类也可响应命令消息。
4.中介模式:用一个中介对象来封装一系列的对象交互。中介模式将一系列对象间的多对多的通信转化为中介对象与各个对象的一对多的通信,从而使其耦合松散,而且可以独立的改变它们之间的交互。MFC中对话框类作为中介类,对话框上的其它控件作为同事类。如编辑框,按钮,下拉链表框等。当对话框上的两个或多个控件需要通信时(如编辑框输入有效数据后使按钮使能),发送消息的控件首先将消息发送到它的父窗口(对话框),然后父窗口将收到的消息转发到各目标控件。
符号 | 说明 |
mID | 学号 |
mName | 姓名 |
mAge | 年龄 |
mAddr | 地址 |
mMAth | 数学成绩 |
mCpp | C++成绩 |
mCount | 学生人数 |
mSex | 性别 |
mListBox | 对话框 |
OnClickedButtonAdd() | 增加按钮 |
OnClickedButtonDelete() | 删除按钮 |
OnClickedButtonModify() | 修改按钮 |
Sort | 排序函数 |
OnClickedButtonMathSort() | 数学成绩排序 |
OnClickedButtonCppSort() | C++成绩排序 |
OnClickedButtonRandAdd() | 随机生成按钮 |
OnBnClickedOk() | 确定键 |
OnSelchangeList1 | 响应事件函数 |
DestroyWindow() | 销毁指定窗口 |
OnInitDialog() | 初始化对话框 |
StudentInfoManagementDoc.cpp
#include "StudentInfoDlg.h"
void CStudentInfoManagementDoc::OnInputButton()
{
// TODO: 在此添加命令处理程序代码
StudentInfoDlg sid;
if (sid.DoModal() == IDOK)//显示一个模态对话框,当对话框关闭时,他会返回一个值如果你是按确定按钮关闭的对话框,则返回IDOK(这是系统内部定义的一个宏值)如果是按取消按钮关闭的对话框,则返回IDCANCEL
{
TCHAR strExePath[MAX_PATH];
GetModuleFileName(NULL, strExePath, MAX_PATH);//获取当前目录
strExePath[MAX_PATH - 1] = '\0';//把文件变成字符串
CString sFilePath = strExePath;
int nPos = sFilePath.ReverseFind(_T('.'));//从后面往前找小数点
sFilePath = sFilePath.Left(nPos + 1) + _T("txt");//更改文件后缀为txt
mMaths.RemoveAll(); mCpps.RemoveAll();
std::ifstream ifile(sFilePath, std::ios_base::in);//读取文件
if (ifile)//读取成功
{
int size;
ifile >>size;
for (int k = 0; k< size; ++k)
{
Student* pStudent = new Student();//创建指针学生
ifile >>(*pStudent);
mMaths.Add(pStudent->GetMath());
mCpps.Add(pStudent->GetCpp());
delete pStudent;
}
}
ifile.close();
auto pos = GetFirstViewPosition();
GetNextView(pos)->InvalidateRect(nullptr, true);
}
}
BOOL CStudentInfoManagementDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
// TODO: 在此添加您专用的创建代码
mMaths.RemoveAll();//删除数据
mCpps.RemoveAll();
std::ifstream ifile;
ifile.open(lpszPathName, std::ios_base::in);
if (ifile)
{
int size;
ifile >>size;
for (int k = 0; k< size; ++k)
{
Student* pStudent = new Student();
ifile >>(*pStudent);
mMaths.Add(pStudent->GetMath());//增加数据
mCpps.Add(pStudent->GetCpp());
delete pStudent;
}
}
ifile.close();
auto pos = GetFirstViewPosition();
GetNextView(pos)->InvalidateRect(nullptr, true);//向指定的窗体更新区域添加一个矩形
return TRUE;
}
Student.h
#include
#include
enum class Sex { male, female };//枚举类型,举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围
class Student
{
public:
Student();
~Student();
unsigned int GetID()const { return mID; }//获取学号
void SetID(unsigned int nID) { mID = nID; }//设置学号
std::string GetName()const { return mName; }//获取名字
void SetName(const std::string& sName) { mName = sName; }//设置名字
Sex GetSex()const { return mSex; }//获取性别
void SetSex(Sex nSex) { mSex = nSex; }//设置性别
unsigned int GetAge()const { return mAge; }//获取年龄
void SetAge(unsigned int nAge) { mAge = nAge; }//设置年龄
std::string GetAddr()const { return mAddr; }//获取地址
void SetAddr(const std::string& sAddr) { mAddr = sAddr; }//设置地址
float GetMath()const { return mMath; }//获取数学成绩
void SetMath(float nMath) { mMath = nMath; }//设置数学成绩
float GetCpp()const { return mCpp; }//获取C++成绩
void SetCpp(float nCpp) { mCpp = nCpp; }//设置C++成绩
friend std::ostream& operator<<(std::ostream& os, const Student& st);//重载<<
friend std::istream& operator>>(std::istream& is, Student& st);//重载>>
private:
unsigned int mID;
std::string mName;
Sex mSex;
unsigned int mAge;
std::string mAddr;
float mMath;
float mCpp;
};
Student.cpp
#include "Student.h"
Student::Student()
:mID(0)
, mName("")
, mSex(Sex::male)
, mAge(18)
, mAddr("")
, mMath(0.0f)
, mCpp(0.0f){}
Student::~Student(){}
std::ostream& operator<<(std::ostream& os, const Student& st)//输出数据
{
os << " " << st.mID;
os << " " << st.mName;
os << " " << (int)st.mSex;
os << " " << st.mAge;
os << " " << st.mAddr;
os << " " << st.mMath;
os << " " << st.mCpp;
return os;
}
std::istream& operator>>(std::istream& is, Student& st)//输入数据
{
is >>st.mID;
is >>st.mName;
int nSex;
is >>nSex;
st.mSex = nSex == 0 ? Sex::male : Sex::female;
is >>st.mAge;
is >>st.mAddr;
is >>st.mMath;
is >>st.mCpp;
return is;
}
StudentInfoDlg.h
#include "string"
#include "Student.h"
#include
#include
class StudentInfoDlg : public CDialogEx
{
public:
void Sort(bool(*pred)(const Student*, const Student*));//Sort函数并非命令
};
StudentInfoDlg.cpp
void StudentInfoDlg::OnClickedButtonAdd()//添加信息
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();//把对话框输入的数据放入变量
if (mID != 0 && !mName.IsEmpty())//判断该学生是否存在,如果不存在,则创建一个新的
{
Student* pStudent = new Student();//生成指针学生对象,在头文件类处增加class Student;
pStudent->SetID(mID);
pStudent->SetName(std::string(mName));//此处要注意字符转换,调试属性处修改
pStudent->SetSex(mSex == 0 ? Sex::male : Sex::female);//选男为0,选女为1
pStudent->SetAge(mAge);
pStudent->SetAddr(std::string(mAddr));
pStudent->SetMath(mMath);
pStudent->SetCpp(mCpp);
CString sTemp;//存放学生学号和姓名
sTemp.Format(_T("%d:%s"), pStudent->GetID(), pStudent->GetName().c_str());//字符串转换,十进制整数,字符串
mListBox.AddString(sTemp);//向ListBox,即左边列表,里添加变量的内容
mListBox.SetItemDataPtr(mListBox.GetCount() - 1, pStudent);//将一个32位的指针与列表框一个item联系,获取item,即获取该学生信息
mName = "";//清空对话框信息
mAddr = "";
mMath = 0.0f;
mCpp = 0.0f;
UpdateData(FALSE);//UpdateData(TRUE)是将窗口中的信息数据更新到程序变量,UpdateData(FALSE)是将程序中的更改在界面上更新显示。
}
}
void StudentInfoDlg::OnClickedButtonDelete()//删除信息
{
// TODO: 在此添加控件通知处理程序代码
int sel = mListBox.GetCurSel();//获取选择的数据,用于取得组合框中当前选中的项的字符串。
if (sel == LB_ERR)return;//LB_ERR=-1,如果没有选择,退出
Student* pStudent = (Student*)mListBox.GetItemDataPtr(sel);//获取列表框中的数据,强制转换成学生类指针
mListBox.DeleteString(sel);//删除列表数据
delete pStudent;
}
void StudentInfoDlg::OnClickedButtonModify()//修改按钮
{
// TODO: 在此添加控件通知处理程序代码
int sel = mListBox.GetCurSel();//选择对象,sel是行号,从0开始
if (sel == LB_ERR)return;//LB_ERR=-1,如果没有选择,退出
Student* pStudent = (Student*)mListBox.GetItemDataPtr(sel);
UpdateData();//MFC的窗口函数,用来刷新数据的。
pStudent->SetID(mID);
pStudent->SetName(std::string(mName));
pStudent->SetSex(mSex == 0 ? Sex::male : Sex::female);
pStudent->SetAge(mAge);
pStudent->SetAddr(std::string(mAddr));
pStudent->SetMath(mMath);
pStudent->SetCpp(mCpp);
CString sTemp;
sTemp.Format(_T("%d,%s"), pStudent->GetID(), pStudent->GetName().c_str());
mListBox.DeleteString(sel);//先删除数据
mListBox.InsertString(sel, sTemp);//在原来的位置插入新的数据
mListBox.SetItemDataPtr(sel, pStudent);
mName = "";//清空对话框信息
mAddr = "";
mMath = 0.0f;
mCpp = 0.0f;
UpdateData(FALSE);
}
void StudentInfoDlg::Sort(bool(*pred)(const Student*, const Student*))//排序函数
{
// TODO: 在此添加命令处理程序代码
std::list
int n = mListBox.GetCount();//学生人数
for (int k = 0; k< n; ++k)
{
Student* pSt = (Student*)mListBox.GetItemDataPtr(k);
sts.push_back(pSt);//在容器尾端插入学生数据
}
sts.sort(pred);//stl中的sort函数可以对给定区间的数据进行排序
while (mListBox.GetCount() >0)mListBox.DeleteString(mListBox.GetCount() - 1);
for (auto st : sts)//添加
{
CString sTemp;
sTemp.Format(_T("%d,%s"), st->GetID(), st->GetName().c_str());
mListBox.AddString(sTemp);
mListBox.SetItemDataPtr(mListBox.GetCount() - 1, st);
}
}
void StudentInfoDlg::OnClickedButtonMathSort()
{
// TODO: 在此添加控件通知处理程序代码
Sort([](const Student* st1, const Student* st2) { return st1->GetMath() >st2->GetMath(); });
}
void StudentInfoDlg::OnClickedButtonCppSort()
{
// TODO: 在此添加控件通知处理程序代码
Sort([](const Student* st1, const Student* st2) { return st1->GetCpp() >st2->GetCpp(); });
}
void StudentInfoDlg::OnClickedButtonRandAdd()//随机生成
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(true);
for (int k = 0; k< mCount; ++k)
{
Student* pStudent = new Student;
pStudent->SetID(2006300000 + rand() % 10000);
TCHAR name[9];//随机产生8个字母作为名字
for (int i = 0; i< 8; ++i)name[i] = 65 + rand() % 26;
name[8] = _T('\0');
pStudent->SetName(std::string(name));
pStudent->SetSex(rand() % 2 == 0 ? Sex::male : Sex::female);
pStudent->SetAge(18 + rand() % 3);
pStudent->SetAddr(std::string(name));
pStudent->SetMath(40.0f + (float)(rand() % 61));
pStudent->SetCpp(40.0f + (float)(rand() % 61));
CString sTemp;
sTemp.Format(_T("%d,%s"), pStudent->GetID(), pStudent->GetName().c_str());
mListBox.AddString(sTemp);
mListBox.SetItemDataPtr(mListBox.GetCount() - 1, pStudent);
}
}
void StudentInfoDlg::OnBnClickedOk()//确定键,同时存储文件
{
// TODO: 在此添加控件通知处理程序代码
TCHAR filePath[MAX_PATH];
GetModuleFileName(NULL, filePath, MAX_PATH);//获取当前目录
CString sFileName = filePath;
int nPos = sFileName.ReverseFind(_T('.'));
sFileName = sFileName.Left(nPos + 1) + _T("txt");//更改文件后缀为txt
std::ofstream ofile(sFileName, std::ios_base::out);//打开文件
if (ofile)//打开成功
{
int nCount = mListBox.GetCount();//获取行数
ofile<< nCount<< " ";//写入文件
for (int k = 0; k< nCount; ++k)
{
auto pStudent = (Student*)mListBox.GetItemDataPtr(k);//保存指针
ofile<< (*pStudent);
}
}
ofile.close();//关闭文件
CDialogEx::OnOK();
}
void StudentInfoDlg::OnSelchangeList1()//鼠标在左边选中学生,右边显示具体信息
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();
int sel = mListBox.GetCurSel();//选择对象,sel是行号,从0开始
if (sel == LB_ERR)return;//LB_ERR=-1,如果没有选择,退出
Student* pSt = (Student*)mListBox.GetItemDataPtr(sel);//强制转换成学生类指针
mID = pSt->GetID();
mName = pSt->GetName().c_str();
mSex = pSt->GetSex() == Sex::male ? 0 : 1;
mAge = pSt->GetAge();
mAddr = pSt->GetAddr().c_str();
mMath = pSt->GetMath();
mCpp = pSt->GetCpp();
UpdateData(FALSE);//更新窗口
}
BOOL StudentInfoDlg::DestroyWindow()//窗口关闭时释放所有指针
{
// TODO: 在此添加专用代码和/或调用基类
for (int k = 0; k< mListBox.GetCount(); ++k)delete(Student*)mListBox.GetItemDataPtr(k);
return CDialogEx::DestroyWindow();
}
BOOL StudentInfoDlg::OnInitDialog()//窗口初始化函数读出文件
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化
TCHAR strExePath[MAX_PATH];
GetModuleFileName(NULL, strExePath, MAX_PATH);//查找可执行文件所在的目录
strExePath[MAX_PATH - 1] = '\0';//把文件变成字符串
CString sFilePath = strExePath;//打开文件
int nPos = sFilePath.ReverseFind(_T('.'));//从后面往前找小数点
sFilePath = sFilePath.Left(nPos + 1) + _T("txt");//更改文件后缀
std::ifstream ifile;
ifile.open(sFilePath, std::ios_base::in);//读取文件
if (ifile)//读取成功
{
int size;
ifile >>size;
for (int k = 0; k< size; ++k)
{
Student* pStudent = new Student();//生成新的对象
ifile >>(*pStudent);//读取指针
CString sTemp;//用数组存储数据
sTemp.Format(_T("%d,%s"), pStudent->GetID(), pStudent->GetName().c_str());
mListBox.AddString(sTemp);//添加到左边框
mListBox.SetItemDataPtr(k, pStudent);//保存指针
}
}
// TODO: 在此添加额外的初始化
ifile.close();
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
StudentInfoManagementDoc.h
class CStudentInfoManagementDoc : public CDocument
{
// 特性
public:
CArray
CArray
};
StudentInfoManagementView.cpp
void CStudentInfoManagementView::OnDraw(CDC* pDC)//画图函数,绘制方形图和折线图
{
CStudentInfoManagementDoc* pDoc = GetDocument();//获取对话框中的数据
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
//统计成绩各个分数段人数和大值
int counts[][5] = { {0,0,0,0,0},{0,0,0,0,0} };//定义二维数组,记录每分段学生
int mathMax = -1, cppMax = -1;
for (int k = 0; k< pDoc->mMaths.GetCount(); ++k)
{
int n = (int)pDoc->mMaths[k] / 10 - 5;//判断当前学生的数学成绩属于哪个分数段,处理数学数据,60-100分四段,60以下一段
if (n< 0)n = 0;
else if (n >4)n = 4;
++counts[0][n];//相应分数段的人数+1
if (mathMax< counts[0][n])mathMax = counts[0][n];//最高分数
n = (int)pDoc->mCpps[k] / 10 - 5;//判断当前学生的C++成绩属于哪个分数段,处理C++数据,60-100分四段,60以下一段
if (n< 0)n = 0;
else if (n >4)n = 4;
++counts[1][n];//相应分数段的人数+1
if (cppMax< counts[1][n])cppMax = counts[1][n];//最高分数
}
CRect rect;//获取客户区矩形
GetClientRect(&rect);
int w = rect.Width(), h = rect.Height() / 2;//窗口宽度和高度
pDC->MoveTo(0, h);//窗口分为上下两部分
pDC->LineTo(w, h);
//显示标注
CRect rt(0, 0, w, 20);
pDC->DrawText("数学成绩统计分布图", rt, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在区域内显示文本,水平居中,垂直居中,单行显示
rt = CRect(0, h, w, h + 20);
pDC->DrawText("C++成绩统计分布图", rt, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在区域内显示文本,水平居中,垂直居中,单行显示
//获取画笔和画刷
CPen pen;
pen.CreatePen(PS_SOLID, 2, RGB(0x00, 0x00, 0xFF));
auto oldPen = pDC->SelectObject(&pen);
CBrush brush0;
brush0.CreateHatchBrush(HS_BDIAGONAL, RGB(0x00, 0x12, 0x7C));
CBrush brush1;
brush1.CreateHatchBrush(HS_BDIAGONAL, RGB(0x00, 0x12, 0x7C));
CPoint pts[2][5];//画折线数据
int dw = 80, dh = 20;//每个矩形的宽度及每个人的高度
int x = (w - 5 * dw) / 2;//设置起始位置
for (int k = 0; k< 5; ++k)
{
if (k % 2 == 0)pDC->SelectObject(&brush0);
else pDC->SelectObject(&brush1);
for (int i = 0; i< 2; ++i)
{
int y = (i + 1) * h - dh * counts[i][k];
rt = CRect(x, (i + 1) * h, x + dw, y);
pDC->Rectangle(rt);
CString temp;
temp.Format(_T("%d人"), counts[i][k]);//在矩形中显示人数
pDC->DrawText(temp, rt, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在区域内显示格式化文本,水平居中,垂直居中,单行显示
pts[i][k].x = x + dw / 2;
pts[i][k].y = y;
}
x += dw;
}
CPen redPen;//定义红色画笔
redPen.CreatePen(PS_SOLID, 3, RGB(0xFF, 0x00, 0x00));
pDC->SelectObject(&redPen);
for (int k = 0; k< 2; ++k)pDC->Polyline(pts[k], 5);//绘制红色折线
pDC->SelectObject(oldPen);
}
实验结果分析:
实验所设计的程序,应用于学生系统管理,十分直观,既方便查询学生信息,对学生成绩进行排序,也能统计各分数段的人数情况,具有较强的实用性。
实验收获和体会:
本次课程设计是对前面所学知识的总结,对学生能力有一定的要求,通过一个比较完整的应用程序的设计,将学过的知识连贯起来,掌握开发一个实际应用程序的步骤,同时学会使用开发工具实现界面友好的应用程序。并通过本实验,掌握如何运用面向对象技术对具体的应用系统进行分析和设计。
实验过程中,我遇到了一些问题,比如排序程序方面掌握不足,vs2019中的程序应把”#include "pch.h" ”放在程序最前面,要注意MFC中的字符串转换等等。
我遇到很多bug,通过老师提供的相关资料及请教老师,最终完成了这个程序,从其中学到了许多,进一步熟练掌握了类的运用及一些有关MFC的知识。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧