(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统

程序设计
课程设计实验报告

学院: 计算机科学与网络工程学院
专业班级: XXX
姓名: XXX
学号: 190XXXXXXX

2020.6.29

一、课程设计题目及内容

题目:学生成绩管理系统
设计要求及提示如下:
(1)、设计一个学生类Student,包括数据成员:姓名、学号、二门课程(面向对象程序设计、高等数学)的成绩。
(2)、创建一个管理学生的类Management,包括实现学生的数据的增加、删除、修改、按课程成绩排序、保存学生数据到文件及加载文件中的数据等功能。
(3)、创建一个基于对话框的MFC应用程序,程序窗口的标题上有你姓名、学号和应用程序名称。使用(1)和(2)中的类,实现对学生信息和成绩的输入和管理。
(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第1张图片

(4)、创建一个单文档的MFC应用程序,读取(3)中保存的文件中的学生成绩,分别用直方图和折线方式显示所有学生某课程的成绩分布图。
(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第2张图片

二、每个功能模块的设计分析及算法描述

各个功能模块的核心功能在Management类完成:

  1. 在Management类里声明了一个动态数组all,默认初始化容量为10个,在添加数据时,首先进行数据合法性检查,如果检查不通过直接添加失败,通过则进行添加。如果数组容量满了,将进行自动扩容,扩容的方法是:再开辟一个更大容量的动态数组temp,然后逐一将all中的数据拷贝到temp,拷贝完成之后释放掉all的内存空间,再让all指向temp。如果数组容量再次满了,重复上述操作。
  2. 删除数据时,需要传入学号,是先逐一遍历all数组中的元素,当发现某个Student与传入的学号相同时,就将这这后面的所有Student向前挪一位,覆盖掉要被删除的Student。
  3. 修改数据,分为修改姓名、学号、程序课成绩、高数课成绩四个函数,这四个函数都是需要传入学号和修改后的数据,方法和删除数据一样,先根据id找到要修改的Student,然后修改。
  4. 排序功能:采用冒泡排序。
  5. 加载文件中的数据:在构造函数里初始化数组all,使用ifstream读取和程序相同目录下的“data.txt”文件,逐个读取后封装成Student对象,添加到数组all。
  6. 保存数据到文件:在析构函数里进行,先用ofstream打开“data.txt”文件,然后遍历all数组,分别文件输出姓名、学号、程序课成绩、高数课成绩,每个信息之间用空格分隔,每保存完一个Student后回车。
    data.txt文件如图:
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第3张图片

画统计图在View类完成:

  1. 排序之前,从Management类里获取要统计的信息(高数课成绩或者程序课成绩),vector类型,然后再使用这些数据来统计。
  2. 绘制条形图,先创建一个矩形,把vector中的数据分为5个区间,根据矩形的宽度、高度和区间的最高人数计算每个人应该占多少高度、每个区间占多少宽度,然后根据这些信息来绘制。
  3. 绘制折线图,也是先创建一个矩形,然后在这个矩形里画线(10行,n列,n为数据的数量),用矩形的宽度/数据的数量可以得到每个点相差的距离,用矩形的高度*数据的值/100得到高度,然后每个点的信息都记录在数组里,最后交给Polyline函数画线。

查找功能:首先要检查输入内容的合法性(分数区间不能为空,最大值不能大于100,最小值不能小于0,且最小值不能比最大值大),借助vector容器,遍历Management的all数组,如果满足条件,则将改Student添加到vector容器中,最后遍历完之后输入vector容器保存的结果。模糊查询的方法是用CString的Find()函数,如果返回结果不为-1,则代表这个CString里包含这个字符串。

三、程序中使用的数据及主要符号说明

Student 类:
	CString m_name;//姓名
	CString m_id;//学号(唯一)
	int score1;//程序课成绩
	int score2;//高数课成绩

Management 类:
	Student* all; //存放学生数据的数组
	int capacity; //数组当前容量
	int size;     //数组当前大小
	(extern) Management m; //Management对象,在View.cpp和ADD.cpp都有出现,为了在两个cpp共用一个对象,所以在其中一个地方加了extern

数据管理的对话框:
	ADD; //添加数据的对话框,类名为ADD
	CString m_name;	//输入姓名的编辑框
	CString m_id;//输入学号的编辑框
	int score1;//输入程序课成绩的编辑框
	int score2;//输入高数课成绩的编辑框
	int Search_Score1_Min; //数据查找时,需要输入的程序课成绩最小值
	int Search_Score1_Max; //数据查找时,需要输入的程序课成绩最大值
	int Search_Score2_Min; //数据查找时,需要输入的高数课成绩最小值
	int Search_Score2_Max; //数据查找时,需要输入的高数课成绩最大值
	CString Search_Name; //数据查找时,需要输入的姓名
	CString Search_Id; //数据查找时,需要输入的程序课成绩学号

四、带有详细注释的自己编写的源程序

//Student.h
#pragma once

class Student {
     
public:	
	CString m_name;//姓名
	CString m_id;//学号(唯一)
	int score1;//程序课
	int score2;//高数课

	//全参构造
	Student(const CString& m_name, const CString& m_id, int score1, int score2);
	//默认构造
	Student();
};

Student.cpp
#include "pch.h"
#include "Student.h"

Student::Student(const CString& m_name, const CString& m_id, int score1, int score2)
	: m_name(m_name),
	  m_id(m_id),
	  score1(score1),
	  score2(score2) {
     
}

Student::Student() {
     
}

//Management.h
#pragma once
#include
#include "Student.h"
#include 
#include "ADD.h"
#include 
using namespace std;
constexpr auto INIT_SIZE = 10;	//默认大小是10个;
constexpr auto INCREASE_SIZE = 10;	//数组满后扩充的容量;

class Management {
     
public:
	Student* all = NULL; //存放学生数据的数组,在构造函数中对其初始化
	int capacity = 0;   //数组当前容量
	int size = 0;       //当前大小
	//--------------------------------------------------------
	//基本功能
	void add(Student stu);    //添加数据
	bool del(CString id);     //根据ID删除数据
	bool hasId(CString id);   //判断id是否存在
	void sortByScore1(bool = false);      //按程序课成绩排序(false:降序,true:升序,采用了默认参数,默认降序)
	void sortByScore2(bool = false);      //按高数课成绩排序(false:降序,true:升序,默认降序)
	void sortById(bool = false);		//按学号排序(false:降序,true:升序,默认降序)
	//---------------------------------------------------------
	//根据id获取名字、分数
	CString getName(CString id);
	int getS1(CString id);	//获取程序课成绩
	int getS2(CString id);    //获取高数课成绩
	//---------------------------------------------------------
	//根据id来修改信息
	bool changeData(CString oldId, CString newName, CString newId, int S1, int S2) const;
	//---------------------------------------------------------
	//构造和析构函数
	Management();
	~Management();
	//--------------------------------------------------------
	//获取高数和程序课的vector集合(用于画统计图)
	vector<int> getMathScores() const;
	vector<int> getProgrammingScores() const;
};

//Management.cpp
#include "pch.h"
#include "Management.h"


//添加数据
void Management::add(Student stu) {
     

	if (size >= capacity) {
     
		//如果当前数组存满了
		capacity += INCREASE_SIZE;
		Student* temp = new Student[capacity]; //重新创建一个容量更大的数组
		for (int i = 0; i < size; i++) {
     
			temp[i] = all[i]; //把旧数组的数据迁移到新数组
		}
		delete[] all; //把旧数组的空间释放掉
		all = temp; //让all指针指向新数组
	}
	all[size++] = stu; //插入数据
}

//删除数据
bool Management::del(CString id) {
     
	for (int i = 0; i < size; i++) {
     
		//寻找指定id
		if (id == all[i].m_id) {
     
			//让后面的元素往前移,填补中间被删除后空缺的位置
			for (int j = i; j < size - 1; ++j) {
     
				all[j] = all[j + 1];
			}
			--size;
			return true;
		}
	}
	//如果找不到指定的id,删除失败
	return false;
}

//判断是否存在指定的id
bool Management::hasId(CString id) {
     
	for (int i = 0; i < size; i++) {
     
		if (id == all[i].m_id)
			return true;
	}
	return false;
}


//按照程序课成绩排序(false:降序,true:升序,采用默认参数,默认降序)
void Management::sortByScore1(bool tag) {
     
	//采用冒泡排序算法
	if (tag) {
     
		//升序
		for (int i = 0; i < size; i++) {
     
			for (int j = 0; j < size - i - 1; ++j) {
     
				if (all[j].score1 > all[j + 1].score1) {
     
					Student temp = all[j];
					all[j] = all[j + 1];
					all[j + 1] = temp;
				}
			}
		}
	}else {
     
		//降序
		for (int i = 0; i < size; i++) {
     
			for (int j = 0; j < size - i - 1; ++j) {
     
				if (all[j].score1 < all[j + 1].score1) {
     //和上面的不同是这里< ,上面 >
					Student temp = all[j];
					all[j] = all[j + 1];
					all[j + 1] = temp;
				}
			}
		}
	}

}

//按照高数课成绩排序(false:降序,true:升序,默认降序)
void Management::sortByScore2(bool tag) {
     
	//采用冒泡排序算法(代码和上面的函数一样,只是把score1改成score2而已)
	if (tag) {
     
		//升序
		for (int i = 0; i < size; i++) {
     
			for (int j = 0; j < size - i - 1; ++j) {
     
				if (all[j].score2 > all[j + 1].score2) {
     
					Student temp = all[j];
					all[j] = all[j + 1];
					all[j + 1] = temp;
				}
			}
		}
	}
	else {
     
		//降序
		for (int i = 0; i < size; i++) {
     
			for (int j = 0; j < size - i - 1; ++j) {
     
				if (all[j].score2 < all[j + 1].score2) {
     //和上面的不同是这里< ,上面 >
					Student temp = all[j];
					all[j] = all[j + 1];
					all[j + 1] = temp;
				}
			}
		}
	}
}

//按学号排序(false:降序,true:升序,默认降序)
void Management::sortById(bool tag)
{
     
	//采用冒泡排序算法(代码和按程序课排序的函数一样,只是把score1改成m_id而已)
	if (tag) {
     
		//升序
		for (int i = 0; i < size; i++) {
     
			for (int j = 0; j < size - i - 1; ++j) {
     
				if (all[j].m_id > all[j + 1].m_id) {
     
					Student temp = all[j];
					all[j] = all[j + 1];
					all[j + 1] = temp;
				}
			}
		}
	}
	else {
     
		//降序
		for (int i = 0; i < size; i++) {
     
			for (int j = 0; j < size - i - 1; ++j) {
     
				if (all[j].m_id < all[j + 1].m_id) {
     //和上面的不同是这里< ,上面 >
					Student temp = all[j];
					all[j] = all[j + 1];
					all[j + 1] = temp;
				}
			}
		}
	}
}


CString Management::getName(CString id) {
     
	for (int i = 0; i < size; i++) {
     
		if (id == all[i].m_id)

			return all[i].m_name;
	}
	return TEXT("");
}

//获取程序课成绩
int Management::getS1(CString id) {
     
	for (int i = 0; i < size; i++) {
     
		if (id == all[i].m_id)

			return all[i].score1;
	}
	return 0;
}

//获取高数课成绩
int Management::getS2(CString id) {
     
	for (int i = 0; i < size; i++) {
     
		if (id == all[i].m_id)

			return all[i].score2;
	}
	return 0;
}

//修改信息(学号不存在返回false)
bool Management::changeData(CString oldId, CString newName, CString newId, int S1, int S2) const {
     
	for (int i = 0; i < size; i++) {
     
		if (oldId == all[i].m_id) {
     
			all[i].m_name = newName;
			all[i].m_id = newId;
			all[i].score1 = S1;
			all[i].score2 = S2;
			return true;
		}
	}
	return false;
}



//构造函数
Management::Management() {
     
	all = new Student[INIT_SIZE]; //初始化数组
	capacity = INIT_SIZE; //更新容量
	//读取文件
	ifstream ifs("data.txt", ios::in);
	if (ifs.is_open() && ifs.peek() != EOF) {
     
		//确保文件已经打开,并且不是空

		while (ifs.peek() != EOF) {
     
			//一直读到结尾为止
			string name, id;
			int score1, score2;
			ifs >> name >> id >> score1 >> score2; //逐个读取数据
			CString m_name(name.c_str());
			CString m_id(id.c_str());
			Student stu(m_name, m_id, score1, score2); //新建一个Student
			add(stu); //将Student添加到数组
		}
	}
	ifs.close(); //释放资源
}

//析构
Management::~Management() {
     
	//保存文件
	ofstream ofs("data.txt", ios::out);
	if (ofs.is_open()) {
     
		for (int i = 0; i < size; i++) {
     
			Student stu = all[i];
			string name(CT2A(all[i].m_name.GetBuffer())); //将CString转为string
			string id(CT2A(all[i].m_id.GetBuffer()));

			ofs << name << " " << id << " " << stu.score1 << " " << stu.score2;
			if (i != size - 1) ofs << endl;
			//最后一个数据之后不要换行,否则下次读取的时候会读取多一条空数据
		}

	}
	ofs.close();
	delete[] all;
}


vector<int> Management::getMathScores() const {
     
	vector<int> datas;
	for (int i = 0; i < size; i++) {
     
		datas.push_back(all[i].score2);
	}
	return datas;
}


vector<int> Management::getProgrammingScores() const {
     
	vector<int> datas;
	for (int i = 0; i < size; i++) {
     
		datas.push_back(all[i].score1);
	}
	return datas;
}

//ADD.h(对话框类)
#pragma once
// ADD 对话框

class ADD : public CDialogEx
{
     
	DECLARE_DYNAMIC(ADD)

public:
	ADD(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~ADD();
	

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum {
      IDD = ADD_STUDENT };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	
	CString m_name;
	CString m_id;
	int score1;
	int score2;
	
	afx_msg void OnBnClickedButton1();
	CListBox DATA;
	afx_msg void OnBnClickedButton6();
	afx_msg void OnBnClickedButton2();
	afx_msg void OnLbnSelchangeList1();
	afx_msg void OnBnClickedButton3();
	afx_msg void OnBnClickedButton4();
	afx_msg void OnBnClickedButton5();
	afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
	afx_msg void OnBnClickedButton7();
	afx_msg void OnBnClickedButton8();
	afx_msg void OnBnClickedButton9();
	afx_msg void OnBnClickedButton10();
	afx_msg void OnBnClickedButton11();
	afx_msg void OnBnClickedButton12();
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	int Search_Score1_Min;
	int Search_Score1_Max;
	int Search_Score2_Min;
	int Search_Score2_Max;
	CString Search_Name;
	CString Search_Id;
	afx_msg void OnBnClickedButton13();
};

//ADD.cpp
// ADD.cpp: 实现文件
//

#include "pch.h"
#include "StudentManagerSystem.h"
#include "afxdialogex.h"
#include "ADD.h"
#include "ChildView.h"
#include
#include 


Management m; //创建Management对象

// ADD 对话框

IMPLEMENT_DYNAMIC(ADD, CDialogEx)

ADD::ADD(CWnd* pParent /*=nullptr*/)
	: CDialogEx(ADD_STUDENT, pParent)
	  , m_name(_T(""))
	  , m_id(_T(""))
	  , score1(60)
	  , score2(60), Search_Score1_Min(0)
	  , Search_Score1_Max(100)
	  , Search_Score2_Min(0)
	  , Search_Score2_Max(100)
	  , Search_Name(_T(""))
	  , Search_Id(_T("")) {
     
}

ADD::~ADD() = default;


void ADD::DoDataExchange(CDataExchange* pDX) {
     
	CDialogEx::DoDataExchange(pDX);

	DDX_Text(pDX, IDC_EDIT1, m_name);
	DDX_Text(pDX, IDC_EDIT2, m_id);
	DDX_Text(pDX, IDC_EDIT3, score1);
	DDX_Text(pDX, IDC_EDIT4, score2);

	DDX_Control(pDX, IDC_LIST1, DATA);
	DDX_Text(pDX, IDC_EDIT7, Search_Score1_Min);
	DDX_Text(pDX, IDC_EDIT8, Search_Score1_Max);
	DDX_Text(pDX, IDC_EDIT9, Search_Score2_Min);
	DDX_Text(pDX, IDC_EDIT10, Search_Score2_Max);
	DDX_Text(pDX, IDC_EDIT5, Search_Name);
	DDX_Text(pDX, IDC_EDIT6, Search_Id);
}


BEGIN_MESSAGE_MAP(ADD, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &ADD::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON6, &ADD::OnBnClickedButton6)
	ON_BN_CLICKED(IDC_BUTTON2, &ADD::OnBnClickedButton2)
	ON_LBN_SELCHANGE(IDC_LIST1, &ADD::OnLbnSelchangeList1)
	ON_BN_CLICKED(IDC_BUTTON3, &ADD::OnBnClickedButton3)
	ON_BN_CLICKED(IDC_BUTTON4, &ADD::OnBnClickedButton4)
	ON_BN_CLICKED(IDC_BUTTON5, &ADD::OnBnClickedButton5)
	ON_WM_ACTIVATE()
	ON_BN_CLICKED(IDC_BUTTON7, &ADD::OnBnClickedButton7)
	ON_BN_CLICKED(IDC_BUTTON8, &ADD::OnBnClickedButton8)
	ON_BN_CLICKED(IDC_BUTTON9, &ADD::OnBnClickedButton9)
	ON_BN_CLICKED(IDC_BUTTON10, &ADD::OnBnClickedButton10)
	ON_BN_CLICKED(IDC_BUTTON11, &ADD::OnBnClickedButton11)
	ON_BN_CLICKED(IDC_BUTTON12, &ADD::OnBnClickedButton12)
	ON_WM_CREATE()
	ON_BN_CLICKED(IDC_BUTTON13, &ADD::OnBnClickedButton13)
END_MESSAGE_MAP()


// ADD 消息处理程序

// 添加按钮
void ADD::OnBnClickedButton1() {
     

	//判断输入的内容是否为空
	//我这样做的原因是:如果先UpdateData,那数字输入框如果为空,会弹窗
	CString cstr1;
	CString cstr2;
	CString cstr3;
	CString cstr4;
	GetDlgItem(IDC_EDIT1)->GetWindowText(cstr1);
	GetDlgItem(IDC_EDIT2)->GetWindowText(cstr2);
	GetDlgItem(IDC_EDIT3)->GetWindowText(cstr3);
	GetDlgItem(IDC_EDIT4)->GetWindowText(cstr4);
	if (cstr1 == _T("") || cstr2 == _T("") || cstr3 == _T("") || cstr4 == _T("")) {
     
		MessageBox(TEXT("输入的内容不能为空!"));
		return;
	}
	//==================================================================
	UpdateData(TRUE); //将控件中的数据更新到变量
	//==================================================================
	//判断输入的内容是否包含空格
	int a = m_name.Find(TEXT(" ")); //m_name空格所在的索引
	int b = m_id.Find(TEXT(" ")); //查找空格所在的索引
	if (a != -1 || b != -1) {
     
		//如果索引不是-1,代表输入的东西一定包含空格
		MessageBox(TEXT("输入的内容请不要包含空格!"));
		return;
	}

	//==================================================================
	//判断学号是否全为数字,并且长度为10
	if (m_id.GetLength() == 10) {
     
		//如果长度为10,再判断是否每一位都是数字
		for (int i = 0; i < 10; ++i) {
     
			if (!(m_id.GetAt(i) >= '0' && m_id.GetAt(i) <= '9')) {
     
				//只要碰到一个字符不是数字,添加失败
				MessageBox(TEXT("学号必须由数字组成"));
				return;
			}
		}
	}
	else {
     
		//如果长度不为10,添加失败
		MessageBox(TEXT("学号的长度必须为10"));
		return;
	}
	//==================================================================
	//判断成绩是否符合范围
	if (score1 < 0 || score1 > 100 || score2 < 0 || score2 > 100) {
     
		MessageBox(TEXT("分数必须在0到100分之间"));
		return;
	}
	//==================================================================
	//判断要添加的学号是否存在
	if (m.hasId(m_id)) {
     
		MessageBox(TEXT("该学号已重复"));
		return;
	}
	//==================================================================
	//以上条件都满足后,添加成功
	Student stu(m_name, m_id, score1, score2);
	m.add(stu);
	DATA.AddString(m_id + "==" + m_name); //让ListBox添加一条数据
	MessageBox(TEXT("添加成功"));
	DATA.SetCurSel(DATA.GetCount() - 1); //添加之后让ListBox选中刚刚添加的数据
	OnLbnSelchangeList1(); //手动触发ListBox的Change事件

}


// 删除按钮
void ADD::OnBnClickedButton2() {
     

	CString id;
	int i = DATA.GetCurSel(); //获取ListBox正在选中的索引
	if (i != -1) {
     

		DATA.GetText(i, id);
		const int index = id.Find(_T("==")); //‘==’在字符串里的位置
		id = id.Mid(0, index); //列表里显示的格式为 学号==姓名,读取==前面的,就是学号
		if (m.del(id) /*删除数据,如果学号不存在将返回false*/) {
     
			DATA.DeleteString(i); //列表删除对应的项
			MessageBox(TEXT("删除成功"));
			DATA.SetCurSel(i == 0 ? 0 : i - 1); //手动让ListBox选中被删除的前面一项
			OnLbnSelchangeList1(); //手动触发ListBox的Change事件
		}
		else {
     
			MessageBox(TEXT("该学号不存在!可能是由于数据未更新所致,请刷新列表!"));
		}
	}
	else {
     
		//-1代表没选中
		MessageBox(TEXT("当前没有选中任何学生,请先在左边列表中选择!"));
	}

}

/**
 * ListBox的Change事件
 * 功能:当列表框选中某些项的时候,右边显示这个学生的信息
 */
void ADD::OnLbnSelchangeList1() {
     
	// TODO: 在此添加控件通知处理程序代码
	int i = DATA.GetCurSel(); //获取当前ListBox选中的索引
	if (i != -1) {
     
		CString id;
		DATA.GetText(i, id);
		i = id.Find(_T("=="));
		id = id.Mid(0, i); //列表里显示的格式为 学号==姓名,读取==前面的,就是学号
		if (m.hasId(id)) {
     
			m_name = m.getName(id);
			m_id = id;
			score1 = m.getS1(id);
			score2 = m.getS2(id);
			UpdateData(FALSE); //将变量的值同步到控件
		}
		else {
     
			MessageBox(TEXT("该学号不存在!可能是由于数据未更新所致,请刷新列表!"));
		}
	}
}


//编辑按钮
void ADD::OnBnClickedButton3() {
     
	//获取ListBox当前选中的内容,以获取学号信息
	CString id; //学号
	int i = DATA.GetCurSel(); //当前列表框选中的索引
	if (i == -1) {
     
		MessageBox(TEXT("当前没有选中任何学生,请先在左边列表中选择!"));
		return;
	}
	DATA.GetText(i, id);
	const int index = id.Find(_T("==")); //‘==’在字符串里的位置
	id = id.Mid(0, index); //列表里显示的格式为 学号==姓名,读取==前面的,就是学号
	//==================================================================
	//检查空格和学号格式是否正确,这部分和添加数据里面的代码是一样的。
	//==================================================================
	//判断输入的内容是否为空
	//我这样做的原因是:如果先UpdateData,那数字输入框如果为空,会弹窗
	CString cstr1;
	CString cstr2;
	CString cstr3;
	CString cstr4;
	GetDlgItem(IDC_EDIT1)->GetWindowText(cstr1);
	GetDlgItem(IDC_EDIT2)->GetWindowText(cstr2);
	GetDlgItem(IDC_EDIT3)->GetWindowText(cstr3);
	GetDlgItem(IDC_EDIT4)->GetWindowText(cstr4);
	if (cstr1 == _T("") || cstr2 == _T("") || cstr3 == _T("") || cstr4 == _T("")) {
     
		MessageBox(TEXT("输入的内容不能为空!"));
		return;
	}
	//==================================================================
	UpdateData(TRUE); //将控件中的数据更新到变量
	//==================================================================
	//判断输入的内容是否包含空格
	int a = m_name.Find(TEXT(" ")); //m_name空格所在的索引
	int b = m_id.Find(TEXT(" ")); //查找空格所在的索引
	if (a != -1 || b != -1) {
     
		//如果索引不是-1,代表输入的东西一定包含空格
		MessageBox(TEXT("输入的内容请不要包含空格!"));
		return;
	}

	//==================================================================
	//判断学号是否全为数字,并且长度为10
	if (m_id.GetLength() == 10) {
     
		//如果长度为10,再判断是否每一位都是数字
		for (int i = 0; i < 10; ++i) {
     
			if (!(m_id.GetAt(i) >= '0' && m_id.GetAt(i) <= '9')) {
     
				//只要碰到一个字符不是数字,添加失败
				MessageBox(TEXT("学号必须由数字组成"));
				return;
			}
		}
	}
	else {
     
		//如果长度不为10,添加失败
		MessageBox(TEXT("学号的长度必须为10"));
		return;
	}
	//==================================================================
	//判断成绩是否符合范围
	if (score1 < 0 || score1 > 100 || score2 < 0 || score2 > 100) {
     
		MessageBox(TEXT("分数必须在0到100分之间"));
		return;
	}
	//==================================================================
	//判断是否需要修改学号
	//不需要:m_id和当前的学号一致,不作修改
	//需要:m_id和当前的学号不一致,下一步还需判断修改后的学号是否重复
	if (m_id != id && m.hasId(m_id)) {
     
		//如果m_id != id,代表希望修改学号,但是修改学号需要判断学号有没有重复
		MessageBox(TEXT("该学号已重复"));
		return;
	}
	//===================================================================
	//以上条件都满足之后,
	//1.原来的学号存在:修改成功
	//2.原来的学号不存在:修改失败
	if (m.changeData(id, m_name, m_id, score1, score2)) {
     
		//修改数据,如果学号不存在将返回false

		OnBnClickedButton6(); //手动触发“刷新列表”的单击事件
		MessageBox(TEXT("修改成功"));

		//刷新列表之后,ListBox又会变为不选中状态
		//自动选回刚刚选中的那个
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	else {
     
		MessageBox(TEXT("该学号不存在!可能是由于数据未更新所致,请刷新列表!"));
	}

}


//按程序课降序按钮
void ADD::OnBnClickedButton4() {
     

	m.sortByScore1(); //排序
	CString id;
	int i = DATA.GetCurSel(); //记住排序之前选中的索引
	OnBnClickedButton6(); //刷新列表
	//刷新列表之后,ListBox又会变为不选中状态
	//需要自动选回刚刚选中的那个(如果i==-1,代表原来并没有选择)
	if (i != -1) {
     
		DATA.GetText(i, id);
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	OnLbnSelchangeList1(); //触发ListBox的事件,让右边能显示选中的学生的个人信息
}

//按高数课降序按钮(这部分代码和按程序课降序的一样,唯一不同的地方是第一行的排序函数不同)
void ADD::OnBnClickedButton5() {
     
	m.sortByScore2();
	CString id;
	int i = DATA.GetCurSel();
	OnBnClickedButton6();
	if (i != -1) {
     
		DATA.GetText(i, id);
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	OnLbnSelchangeList1();
}

//刷新列表按钮
void ADD::OnBnClickedButton6() {
     
	DATA.ResetContent(); //清空列表
	for (int i = 0; i < m.size; i++) {
     
		CString str;
		str = m.all[i].m_id;
		str += "==";
		str += m.all[i].m_name;
		DATA.AddString(str); //读取数组,逐个插入数据
	}
}

//窗口激活消息(显示窗口后刷新列表)
void ADD::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) {
     
	CDialogEx::OnActivate(nState, pWndOther, bMinimized);

	// TODO: 在此处添加消息处理程序代码
	srand((unsigned long)time(NULL)); //随机数种子
	OnBnClickedButton6();
}


//按程序课升序(这部分代码和降序的一样,唯一不同的地方是第一行的排序函数给了true参数)
void ADD::OnBnClickedButton7() {
     
	m.sortByScore1(true); //排序
	CString id;
	int i = DATA.GetCurSel(); //记住排序之前选中的索引
	OnBnClickedButton6(); //刷新列表
	//刷新列表之后,ListBox又会变为不选中状态
	//自动选回刚刚选中的那个(如果i==-1,代表原来并没有选择)
	if (i != -1) {
     
		DATA.GetText(i, id);
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	OnLbnSelchangeList1(); //触发ListBox的事件,让右边能显示选中的学生的个人信息
}


//按高数课降序(这部分代码和降序的一样,唯一不同的地方是第一行的排序函数给了true参数)
void ADD::OnBnClickedButton8() {
     
	m.sortByScore2(true);
	CString id;
	int i = DATA.GetCurSel();
	OnBnClickedButton6();
	if (i != -1) {
     
		DATA.GetText(i, id);
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	OnLbnSelchangeList1();
}


//按学号降序(和上面的排序是一样的)
void ADD::OnBnClickedButton9() {
     
	m.sortById(); //排序
	CString id;
	int i = DATA.GetCurSel(); //记住排序之前选中的索引
	OnBnClickedButton6(); //刷新列表
	//刷新列表之后,ListBox又会变为不选中状态
	//自动选回刚刚选中的那个(如果i==-1,代表原来并没有选择)
	if (i != -1) {
     
		DATA.GetText(i, id);
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	OnLbnSelchangeList1(); //触发ListBox的事件,让右边能显示选中的学生的个人信息
}

//按学号升序
void ADD::OnBnClickedButton10() {
     
	m.sortById(true); //排序
	CString id;
	int i = DATA.GetCurSel(); //记住排序之前选中的索引
	OnBnClickedButton6(); //刷新列表
	//刷新列表之后,ListBox又会变为不选中状态
	//自动选回刚刚选中的那个(如果i==-1,代表原来并没有选择)
	if (i != -1) {
     
		DATA.GetText(i, id);
		for (int j = 0; j < DATA.GetCount(); j++) {
     
			CString cs;
			DATA.GetText(j, cs);
			if (cs.Mid(0, cs.Find(_T("=="))) == m_id) {
     
				DATA.SetCurSel(j);
				break;
			}
		}
	}
	OnLbnSelchangeList1(); //触发ListBox的事件,让右边能显示选中的学生的个人信息
}

//面向对象分数随机
void ADD::OnBnClickedButton11() {
     
	UpdateData(TRUE);
	score1 = rand() % 101; //0-100之间的随机数
	UpdateData(FALSE); //数据更新到控件
}


//高数分数随机
void ADD::OnBnClickedButton12() {
     
	UpdateData(TRUE);
	score2 = rand() % 101; //0-100之间的随机数
	UpdateData(FALSE); //数据更新到控件
}


int ADD::OnCreate(LPCREATESTRUCT lpCreateStruct) {
     
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码

	return 0;
}

//查询按钮
void ADD::OnBnClickedButton13() {
     
	//判断输入的成绩范围是不是为空的
	CString cstr1;
	CString cstr2;
	CString cstr3;
	CString cstr4;
	GetDlgItem(IDC_EDIT7)->GetWindowText(cstr1);
	GetDlgItem(IDC_EDIT8)->GetWindowText(cstr2);
	GetDlgItem(IDC_EDIT9)->GetWindowText(cstr3);
	GetDlgItem(IDC_EDIT10)->GetWindowText(cstr4);
	if (cstr1 == _T("") || cstr2 == _T("") || cstr3 == _T("") || cstr4 == _T("")) {
     
		MessageBox(TEXT("输入的成绩的范围不能为空!"));
		return;
	}
	//==============================================================
	UpdateData(TRUE);
	//进行数据的合法性判断
	if (Search_Score1_Max > 100 || Search_Score1_Max < 0 || Search_Score1_Min > 100 || Search_Score1_Min < 0 ||
		Search_Score2_Max > 100 || Search_Score2_Max < 0 || Search_Score2_Min > 100 || Search_Score2_Min < 0) {
     
		MessageBox(TEXT("输入的成绩的范围必须在区间[0,100]内!"));
		return;
	}
	if (Search_Score1_Min > Search_Score1_Max || Search_Score2_Min > Search_Score2_Max) {
     
		MessageBox(TEXT("输入的成绩最小值不能比最大值大!"));
		return;
	}
	//===============================================================
	//开始查询
	std::vector<Student> result;
	for (int i = 0; i < m.size; ++i) {
     
		Student stu = m.all[i];
		if (stu.m_name.Find(Search_Name) != -1 &&
			stu.m_id.Find(Search_Id) != -1 &&
			stu.score1 >= Search_Score1_Min &&
			stu.score1 <= Search_Score1_Max &&
			stu.score2 >= Search_Score2_Min &&
			stu.score2 <= Search_Score2_Max) {
     
			//满足条件
			result.push_back(stu);
		}
	}
	CString str;
	for (auto stu : result) {
     
		CString cScore1;
		CString cScore2;
		cScore1.Format(_T("%d"), stu.score1);
		cScore2.Format(_T("%d"), stu.score2);
		str += _T("学生姓名:") + stu.m_name + _T("\t学号:") + stu.m_id + _T("\t程序课成绩:") + cScore1 + _T("\t高数课成绩:") + cScore2 + _T("\n");
	}
	CString cLength;
	cLength.Format(_T("%d"), result.size());
	MessageBox(str,_T("共查询到") + cLength + _T("条结果"));
}

//ChildView.h

// ChildView.h: CChildView 类的接口
//

#pragma once
#include "ADD.h"
#include "Management.h"
#include
using namespace std;


// CChildView 窗口

class CChildView : public CWnd
{
     
// 构造
public:
	CChildView();
	ADD add;

// 特性
public:

// 操作
public:

// 重写
	protected:
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

// 实现
public:
	virtual ~CChildView();

	// 生成的消息映射函数
protected:
	afx_msg void OnPaint();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

	void DrawBar(CDC *dc, std::vector<int> db,bool left = true) const;
	void DrawLine(CDC *dc, std::vector<int> db,bool left = true) const;
	void OnDraw(CDC* pDC);
};

//ChildView.cpp

// ChildView.cpp: CChildView 类的实现
//

#include "pch.h"
#include "framework.h"
#include "ChildView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

extern Management m; //需要和ADD.cpp共用一个Management对象,所以这里要声明extern
// CChildView

CChildView::CChildView() {
     
}

CChildView::~CChildView() {
     
}


BEGIN_MESSAGE_MAP(CChildView, CWnd)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_DRAWITEM()
	ON_WM_DRAWITEM()
END_MESSAGE_MAP()


// CChildView 消息处理程序

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) {
     
	if (!CWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
	                                   ::LoadCursor(nullptr, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1),
	                                   nullptr);

	return TRUE;
}


void CChildView::OnPaint() {
     
	CPaintDC dc(this); // 用于绘制的设备上下文
	// TODO: 在此处添加消息处理程序代码
	// 不要为绘制消息而调用 CWnd::OnPaint()
	OnDraw(&dc);
}

//画条状图,db:vector类的集合,里面存放所有要统计的数据
void CChildView::DrawBar(CDC* dc, std::vector<int> db,bool left) const {
     //left:是否在左边
	CRect rc; //外面的矩形
	GetClientRect(rc); //获取CWnd客户区的尺寸。(让rc占满窗口)
	rc.DeflateRect(30, 15, 10, rc.Height() / 2+15); //x1,y1,x2,y2
	if(left) {
     
		//如果是左边,相应修改一下x2的值
		rc.right = rc.Width() / 2 - 15;
		dc->TextOutW(rc.left, 0, TEXT("高数课成绩统计"));
	}else {
     
		rc.left = rc.Width() / 2 + 15;
		dc->TextOutW(rc.left, 0, TEXT("程序课成绩统计"));
	}
	CBrush brush1(HS_BDIAGONAL, RGB(96, 192, 255)); //向下阴影(从左到右)在45度(右斜),蓝色
	CBrush brush2(HS_FDIAGONAL, RGB(96, 192, 255)); //向上阴影(从左到右)在45度(左斜),蓝色
	CPen pen(PS_INSIDEFRAME, 2, RGB(96, 192, 255)); //
	int n = 5; //定义有五个区间
	int width = rc.Width() / n; //一个区间的宽度

	int s[] = {
     0, 0, 0, 0, 0}; //各区间人数
	//统计各区间人数
	for (int i = 0; i < db.size(); i++) {
     
		if (db[i] < 60) s[0] ++;
		else if (db[i] >= 60 && db[i] < 70) s[1]++;
		else if (db[i] >= 70 && db[i] < 80) s[2]++;
		else if (db[i] >= 80 && db[i] < 90) s[3]++;
		else s[4]++;
	}

	//找出人数最大的区间
	int max_s = s[0];
	for (int i = 0; i < n; i++)
		if (max_s < s[i]) max_s = s[i];

	int per_Height = rc.Height() / max_s; //算出一个人代表的矩形高度

	CRect ps_rect(rc); //直方图的矩形
	ps_rect.right = ps_rect.left + width;

	dc->SelectObject(&pen); //选择画笔
	CString str[5] = {
     _T("<60"),_T("60-70"),_T("70-80"),_T("80-90"),_T(">=90")}; //将每个区间的注释先存储
	for (int i = 0; i < n; i++) {
     
		ps_rect.top = ps_rect.bottom - per_Height * s[i]; //该区间的高度
		if (i % 2) dc->SelectObject(&brush2);
		else dc->SelectObject(&brush1); //每隔一个区间用不同的斜线,左斜或右斜
		dc->Rectangle(ps_rect); //绘制矩形

		if (s[i] > 0) {
     
			CString str1;
			str1.Format(_T("%d人"), s[i]); //设置str1的内容
			dc->DrawText(str1, ps_rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //在矩形中间显示人数
		}
		//输出注释在矩形下面
		dc->TextOutW((ps_rect.left + ps_rect.right) / 2 - 15, ps_rect.bottom+5 , str[i]);
		ps_rect.OffsetRect(width, 0); //移动矩形框的位置,水平方向移动width
	}

}

//折线图
void CChildView::DrawLine(CDC* dc, std::vector<int> db,bool left) const {
     
	CRect rc; //最外面的矩形
	GetClientRect(rc); //让矩形占满窗口
	rc.DeflateRect(30, rc.Height() / 2 + 15, 10, 15); x1,y1,x2,y2
	if (left) {
     
		//如果是左边,相应修改一下x2的值
		rc.right = rc.Width() / 2 - 15;
	}
	else {
     
		rc.left = rc.Width() / 2 + 15;
	}
	int gridXnums = db.size(); //列数
	int gridYnums = 10; //行数
	int dx = rc.Width() / gridXnums; //每一个格子的宽度
	int dy = rc.Height() / gridYnums; //每一个格子的高度


	CRect gridRect(rc.left, rc.top, rc.left + dx * gridXnums, rc.top + dy * gridYnums);

	CPen gridPen(PS_DASHDOTDOT, 1, RGB(96, 192, 255)); //获取蓝色的点状画笔
	dc->SelectObject(&gridPen); //选择画笔
	//-------------------------------------------------------------------
	//画格子
	for (int i = 0; i <= gridXnums; i++) {
     
		dc->MoveTo(gridRect.left + i * dx, gridRect.bottom);
		dc->LineTo(gridRect.left + i * dx, gridRect.top);
	}
	for (int j = 0; j <= gridYnums; j++) {
     
		dc->MoveTo(gridRect.left, gridRect.top + j * dy);
		dc->LineTo(gridRect.right, gridRect.top + j * dy);
		CString str;
		str.Format(_T("%d"), 100 - 10 * j);
		dc->TextOutW(rc.left - 25, gridRect.top + j * dy - 10, str); //标注纵坐标的值
	}
	//-------------------------------------------------------------------

	gridPen.Detach(); //官方解释:从CGdiObject对象分离Windows GDI对象,并将句柄返回给Windows GDI对象。
	//但是不加这一行会报异常

	CPen linePen(PS_SOLID, 2, RGB(255, 0, 0)); //创建一个红色实线画笔,用于画线
	dc->SelectObject(&linePen); //选择画笔

	int nCount = db.size(); //一共多少个数据
	int deta = gridRect.Width() / nCount; //每一个数据所占的宽度
	POINT* pt = new POINT[nCount];

	for (int i = 0; i < nCount; i++) {
     
		pt[i].x = gridRect.left + i * deta;
		pt[i].y = gridRect.bottom - (int)(db[i] / 100.0 * gridRect.Height());

		CString score;
		score.Format(_T("%d"), db[i]); //设置score的内容
		// dc->TextOutW(pt[i].x - 5, gridRect.bottom + 5, score); //显示分数(在下面)
	}
	dc->Polyline(pt, nCount); //画线
	delete[] pt;
	pt = nullptr;
}


void CChildView::OnDraw(CDC* pDC) {
     
	vector<int> programmingScores = m.getProgrammingScores();
	vector<int> mathScores = m.getMathScores();
	

	if (!mathScores.empty() && !programmingScores.empty()) {
     
		//只有数据不为空了才去画图
		DrawBar(pDC, mathScores,true); //左边条形图画高数
		DrawBar(pDC, programmingScores,false); //右边条形图画程序
		DrawLine(pDC, mathScores,true); //左边折线图画高数
		DrawLine(pDC, programmingScores,false); //右边折线图画程序
	}
}

//左
void CChildView::OnLButtonDown(UINT nFlags, CPoint point) {
     

	CWnd::OnLButtonDown(nFlags, point);

	//弹出对话框
	add.DoModal();

	//重绘统计图
	Invalidate(); // 重画窗口。
	OnPaint();
}

五、程序运行时的效果图

  1. 统计图

(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第4张图片

  1. 数据管理
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第5张图片

  2. 添加学生时,姓名、学号或成绩为空
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第6张图片

  3. 添加学生时,学号不是数字或者长度不为10

(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第7张图片
(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第8张图片

  1. 如果姓名或学号输入了空格
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第9张图片
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第10张图片

  2. 输入的成绩越界
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第11张图片

  3. 成功添加数据
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第12张图片

  4. 成功删除数据
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第13张图片

  5. 成功修改数据(容错条件和添加数据一样,不再演示)
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第14张图片

  6. 按成绩排序(从折线图看效果)
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第15张图片
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第16张图片
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第17张图片
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第18张图片

  7. 数据查询,查询全部
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第19张图片

  8. 数据查询,查询高数课和程序课成绩都大于80分的
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第20张图片

  9. 数据查询,查询姓名里带有“刘”字的
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第21张图片

  10. 查询学号里带有2的
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第22张图片

六、实验结果分析

我在实验中遇到的问题和解决方法

  1. 对话框初始化时程序崩溃,如下图:
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第23张图片

本来我是打算在对话框一打开就自动加载数据,而不需要手动加载数据,所以我在对话框的OnCreate()函数里进行列表的初始化操作,才会发生这样子的程序奔溃,经过我后面的尝试,把初始化代码放到OnActivate()函数里才解决问题。问题产生的原因我不太清楚,我个人猜测是OnCreate函数在窗口还没完全显示的时候执行,而OnActivate会在窗口显示完成之后才执行,应该是因为窗口还没显示的时候把数据添加到列表导致的问题。

  1. Management对象的问题
    因为我所有的学生数据都存放在Management里的all数组里,所以我需要在对话框ADD类和单文档绘图的View类里共用一个Management对象,一开始我是把对象创建在.h文件中,结果造成了一些错误(无法解析的外部符号),于是我按照网上的办法,把对象放在.cpp文件中。但是新的问题出现了,我本以为多个cpp文件如果共用一个变量,只需在一个.cpp中包含另一个.cpp就行了,结果我在一个debug中,发现Management对象竟然创建了5次,这时我才意识到多个cpp中的Management并不是共用的,后来我的解决办法是在View.cpp中的Management加extern修饰符。

  2. 数据文件保存和读取问题
    我在Management类的构造和析构函数中对数据文件进行读取和保存,我每向文件输出完一条学生信息,我都会换行,但是在读取的时候会莫名地多出一条空白数据,经过我的研究,是因为输出最后一行数据再回车导致的问题。我的解决办法是在向文件输出时加个if判断,如果这条数据不是最后一条数据,则再输出回车。

  3. 关于成绩输入框的弹窗问题
    我成绩输入框创建变量的时候,类型设置为int,当执行UpdataData函数的时候,如果里面不是数字或者为空,会弹出这样的一个框
    (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第24张图片

本来这样的提示是没什么问题的,但是弹出框了之后,如果是添加数据,则照样会添加成功,只是成绩都是默认值0,但这样子就不符合逻辑了!我的解决办法是:先在把编辑框的Number属性改成True,保证了输入框无法输入数字,然后就还剩输入为空的情况,我就通过以下代码,在UpdateData之前检查输入是否为空:
(MFC)广州大学大一下课程设计实验报告-学生成绩管理系统_第25张图片

收获

  1. 更加熟悉MFC编程,学会了在View类的绘图方法,学会了基于对话框的很多内容。
  2. 重新复习了上个学期所学的冒泡排序、动态数组等知识。
  3. 领悟了CString与string的区别,并学会了CString与string的互相转换、其他基本数据类型转换成CString。
  4. 掌握了面向对象的编程思想。

你可能感兴趣的:(广大作业,c++)