大一期末作业 学生管理系统完整源码 索引思想实现(c++,利用map和multimap,非链表 非数组,继承类)

大一期末作业 学生管理系统(c++源码,利用map和multimap,索引思想,非链表 非数组,继承类)


前言

大一期末大作业就是写个学生人员的管理小系统,本来打算是用简单的学生类链表数组写,但是大家都知道链表插入快,查找慢,数组就更不考虑了。然而,对于一个系统 查找是非常重要且频繁的操作。所以想着利用索引思想,就选用map和multimap,恰好一个可重复一个不可重复。
map不可重复,则需要一个基础类给学号类这种不可重复继承
multimap可重复,也需要一个基础类给给姓名类继承
当然,本身学生就是一个基础类

基本思想

map中元素是key-value配对的,要查找一个元素,需要提供它的key。map的内部是以平衡二叉树形式储存的,所以查找的速度为O(logn),速度很快。我们的学生一个属性就是一个key,value就是学生实体。多个属性就有多个map,多个key对应一个实体。虽然我们要多写几个map,但是空间的需求基本的可以解决的,而时间的需求有时候来的更加重要。
MyISAM引擎使用B+Tree作为索引结构,InnoDB一样,所以总是有可取之处。


提示:以下是本篇文章正文内容,下面案例可供参考

一、前提准备

如上述所说:需要准备两个基础类用于继承和一个学生基础类,见代码:

1.1 学生类:这里直接用结构体,因为懒

typedef struct student{
	char num[8];
	string name;
	int classnum;
	double gpa;
	int math;
	int english;
	int chinese;
	int index; 	
	friend ostream& operator <<(ostream& os,const student& s)
	{
	os<<s.num<<"    "<<s.name<<"    "<<s.classnum<<"    "<<setiosflags(ios::fixed)<<setprecision(2)<<s.gpa<<"    "<<s.chinese<<"    "<<s.math<<"    "<<s.english;
    os<<endl;
	return os;
	}
	
}Student;

此处利用友元函数将输出符号重载,以便后面使用(博主太懒了)

1.2 multimap类型的基础类

为了提高泛用性,使用类模板,在基础类里面实现show()和find()。xiugai()和delete()留着继承类实现。
show():翻页功能
find():寻找功能

template<class T>
class Mapbase{
	public:
	multimap<T,Student*> m;
	
	public:
	Mapbase(){
			m.clear(); 
			
	}
	void show(){
		int k=0;
		typename multimap<T,Student*>::iterator iter;
		cout<<endl;
		 for(iter = m.begin(); iter != m.end(); iter++)  {
		 	cout<<iter->first<<"   |   "<<*(iter->second);
		   k++;
		 	if(k%10==0)
		 	{
		 		cout<<"< 上一页"<<"下一页 >"<<"      "<<"esc 退出"<<endl;
				int key, key2;
				key = getch();
				if (key==224) {
				key2 = getch();
					if (key2 == 72||key2== 75){
						
					}
					else if (key2== 80||key2== 77) {
						continue;
					}
			 	}
			 	else if(key==27)
			 	{
			 		return ;
				 }
				 else{
				 	cout<<"请输入正确的操作数";
				 }    
		 }
       }
	}
	void find()
	{
		cout<<endl;
		T temp;
		cout<<kk<<"请输入你要查询的信息";
		cin>>temp;
		typename multimap<T,Student*>::iterator beg= m.lower_bound(temp);
		typename multimap<T,Student*>::iterator end= m.upper_bound(temp);
		while(beg != end)
		{
		cout<<beg->first<<*beg->second;
		++beg;
		}

	};
	void xiugai();
	
	 void deletes();
		~Mapbase(){
			m.clear(); 
	}
};

1.3 map类型的基础类

如multimap一样 只需将multimap改成map,稍作修改即可,此处不贴代码,见谅。

1.4 cmp_str

当使用 char* 作为键值时,默认映射将比较指针,而不是指针的引用。 所以结果将不是我们的预期。 两种解决方案: 使用字符串作为键;或者为程序声明,如以下代码所示。

struct cmp_str
{
    bool operator()(char const *a, char const *b)
    {
        return strcmp(a, b) < 0;
    }
};
配合以下代码使用
map<char*,Student*,cmp_str> m;

二、类继承

按照上述学生结构体的属性(属性较少),设置5个新类继承两个基本类即可,此处展示NUM类,其他类以此类推,需要完整源码,见评论,见谅。

	NUM num;
	NAME na;
	GPA gp;
	CLASSNUM cn;
	INDEX in;

NUM类

class NUM:public Mapbase2<char*>
{
	public:
	map<char*,Student*,cmp_str> m;
	public:
		NUM()
		{		
				for(int i=0;i<n;i++)
				{	
				m.insert(pair<char*,Student*>(s[i].num,&s[i]));
				}
			
		}
	public:
		void find()
		{
			char temp[10];
		cout<<"请输入你要查询的学号";
		cin>>temp;
		if(m[temp]==0)
		{
			cout<<"can 't find";
		}
		else
		{
			cout<<"The result :";
			cout<<*m[temp];
		}
		};
		void xiugai(){
		 	int choose,choose2;
		 	string ss;
		 	double dd;
		 	int ii;
		 	char temp[10];
		 		cout<<"请输入你要修改用户的信息"<<endl;
				cin>> temp;	
				typename map<char*,Student*>::iterator begin= m.find(temp);
				cout<<endl<<*(begin->second)<<endl;
				if(m[temp]==0)
				{
					cout<<"can 't find";
				}
				cout<<"请问你要修改什么信息"<<endl;	
				cout<<"[1]学号"<< "[2]姓名"<< "[3]班级"<< "[4]GPA"<<"[5]语文成绩"<< "[6]数学成绩"<< "[7]英语成绩"<<endl; 
				cin>>choose; 
				Student temps;	
				char *temp3=new char[10];
				switch(choose)
				{
					case 1:
						cout<<"学号不能修改";break;
					case 2:
						cout<<"请输入修改后的信息";
						cin>>ss;
						temps=*(begin->second);
						s[temps.index].name=ss;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						break;
					case 3:
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].classnum=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&temps));
						break;
					case 4:
						cout<<"请输入修改后的信息";
						cin>>dd;
						temps=*(begin->second);
						s[temps.index].gpa=dd;	
						temp3=begin->first;				
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));						
						break;
					case 5:
					
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].chinese=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						
						break;
					case 6:
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].math=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						
						break;
					case 7:
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].english=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						
						break;
				 }
		 }
};

NAME类 GPA类 CLASSNUM类 见评论源码

INDEX类

index这个属性需要额外注意,本身在数据文档里面是没有这个属性的,是在输入时候自动添加的,最后不需要保存这个属性。
有何用:我们知道修改功能不同于删除功能,修改功能要求同步,需要你在第一时刻就完成改动并反馈数据库或文本文件。那么我们怎么知道这条数据在原本数据库中的哪个位置,而index恰好利用顺序输入的这个特性直接在输入时候记录原本的位置,那么在修改的时候就方便的多。

while(!in.eof())
{
	in>>s[n].num>>s[n].name>>s[n].classnum>>s[n].gpa>>s[n].chinese>>s[n].math>>s[n].english;
	s[n].index=n;
	n++;				
}

三 基本操作

添加功能

利用insert函数和pair方法添加,这里需要注意的一旦添加一个学生,那么五个属性类就都要添加相关信息。

cout<<"\n"<<"请输入相关学生信息";
cout<<"\n"<<"学号"<< "\t姓名"<< "\t班级"<< "\tGPA"<<"\t语文成绩"<< "\t数学成绩"<< "\t英语成绩"<<endl;		cin>>s[n].num>>s[n].name>>s[n].classnum>>s[n].gpa>>s[n].chinese>>s[n].math>>s[n].english;
num.m.insert(pair<char*,Student*>(s[n].num,&s[n]));
na.m.insert(pair<string,Student*>(s[n].name,&s[n]));
gp.m.insert(pair<double,Student*>(s[n].gpa,&s[n]));
cn.m.insert(pair<int,Student*>(s[n].classnum,&s[n]));
in.m.insert(pair<int,Student*>(s[n].index,&s[n]));

删除

删除功能需要解决的问题有:
(1)按照什么删除?
(2)删除对象一次性有多个怎么办
问题(1)解决:
本程序选择以学号和姓名删除,因为这两个属性具有代表性,特征性够强烈,总不能用成绩删除吧,一次性出来一堆100分啊60分啊,就难受。
问题(2)解决,本文会予以提示:
名字叫刘伟的不只是一个人,所以本文也为此问题做出解决:
一旦出现多人:
在这里插入图片描述
请问你要删除第几个,假设我们选择删除 第二个

查找

这时候上面已经删除了,我们就利用查找功能找一下子

我们选择姓名,输入刘伟,其他学号班级下表什么的就不演示(见谅)
在这里插入图片描述
欸,我们发现还是有两个,但是有区别,我们把他的学号直接改成了no。为什么这么做?
删除有两种方法:
(1)马上删除,假设我们的数据在数据库里面或者txt文本里面,那么如果即可删除就需要调用一次数据库操作,或者文件操作。又或是,我们在程序初始化的时候先把所有数据加载到一个容器里面,可能也是map可能是数组可能是链表,但是我们要知道,删除本身就带着查询功能,较为繁琐的同时可能还涉及同步异步的问题(你删除之后,你是不是或者要不要马上更新啊此类问题)。
(2)所以我的方法是,学号唯一标识,标记为no。假设他已经删除了,我们只需要在查询修改增加等其他基本操作的时候注意此类问题,比如查询,刚刚我把删除的刘伟显示了,你也可以选择不显示,修改的时候遇到no直接提示无此人等操作来避免相关问题
在最后存储写回数据库或文本文件的时候将学号为no的去除。

cout<<kk<<"按照什么删除";
	cout<<kk<<"[1]学号"<< "\t[2]姓名"<<endl;
	cin>>choose;
	cout<<kk<<"请输入删除信息"; 
	switch(choose)
	{
		case 1:
		cin>>numtemp;
		temps=*num.m[numtemp];
		cout<<temps.index;
		strcpy(s[temps.index].num,"no");
			cout<<s[temps.index];
			break;
		case 2:
			cin>>temp;
				typename multimap<string,Student*>::iterator beg= na.m.lower_bound(temp);
				typename multimap<string,Student*>::iterator end= na.m.upper_bound(temp);
				typename multimap<string,Student*>::iterator begin=beg;
					if(end==beg)
				{
					cout<<"查无此人";
					return ;
				}
				while(beg != end)
				{
						cout<<beg->first<<*beg->second;
						++beg;
				}
			
				 if(na.m.count(temp)>1)
				{
					cout<<endl<<"请问要删除上面的第几个"; 
					cin>>choose2;
					for(int i=0;i<choose2-1;i++)
					{
						begin++;
					 } 
				}
				else {
					
				}
					temps=*(begin->second);
					strcpy(s[temps.index].num,"no");
					cout<<s[temps.index];
			break;
	}

修改

修改函数
各个属性类继承重写了基本类的修改函数。举个例子,NUM的修改函数

		void xiugai(){
		 	int choose,choose2;
		 	string ss;
		 	double dd;
		 	int ii;
		 	char temp[10];
		 		cout<<"请输入你要修改用户的信息"<<endl;
				cin>> temp;	
				typename map<char*,Student*>::iterator begin= m.find(temp);
				cout<<endl<<*(begin->second)<<endl;
				if(m[temp]==0)
				{
					cout<<"can 't find";
				}
				cout<<"请问你要修改什么信息"<<endl;	
				cout<<"[1]学号"<< "[2]姓名"<< "[3]班级"<< "[4]GPA"<<"[5]语文成绩"<< "[6]数学成绩"<< "[7]英语成绩"<<endl; 
				cin>>choose; 
				Student temps;	
				char *temp3=new char[10];
				switch(choose)
				{
					case 1:
						cout<<"学号不能修改";break;
					case 2:
						cout<<"请输入修改后的信息";
						cin>>ss;
						temps=*(begin->second);
						s[temps.index].name=ss;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						break;
					case 3:
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].classnum=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&temps));
						break;
					case 4:
						cout<<"请输入修改后的信息";
						cin>>dd;
						temps=*(begin->second);
						s[temps.index].gpa=dd;	
						temp3=begin->first;				
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));						
						break;
					case 5:
					
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].chinese=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						
						break;
					case 6:
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].math=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						
						break;
					case 7:
						cout<<"请输入修改后的信息";
						cin>>ii;
						temps=*(begin->second);
						s[temps.index].english=ii;
						temp3=begin->first;
						m.erase(begin);
						m.insert(make_pair(temp3,&s[temps.index]));
						
						break;
				 }
		 }

查找

查找功能同修改功能一样
各个属性类继承重写了基本类的查找函数。同上

存储

较简单,见源码

总结

记录一下大一大作业,800行有效代码的小程序

功能皆实现,但是界面做的丑:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值