C++动态链表实现学生信息管理系统

       这个是我在大一暑假写的代码,也就刚刚学完了C++,所以我会很详细的讲解我的思路和代码,这篇文章应该比较适合和我一样C++入门的小白。因为水平有限,所以有很多不足之处,请大家多多指正。

        我是用动态链表处理这些学生信息的,每次都从存储文件中读取学生信息,用动态链表串起来,处理过后再存回文件中。所以这篇文章可以帮助大家复习动态链表。我们一个功能一个功能地来讲述:

     

   我们先来介绍功能1:录入信息 

class Student
{
public:
	Student();  //默认构造函数------67 
	Student(char n[20],char g[20],int number,char m[20],char b[5]);            //有参构造函数 -----102 
	friend istream& operator>>(istream& ,Student&);   //输入 -----116 
	friend ostream& operator<<(ostream& ,Student&);   //输出 -----125 
	void show();                                      //输出学生信息 --------134 
	friend Student* find(int number,Student *p);      //查询------------156 
	void insert(); //插入信息--------------169 
	void remove();  //删除 --------------- 195
	void modify();  //修改 ---------------203 
	void chengji(); //录入成绩 ------------244 
	void xuanke();//选课系统 --------------292 
	int findpaiming(); //排名--------------360 
	friend Student* sortedmerge_1(Student *,Student *); //成绩排序--------- 
	friend Student* sortedmerge_2(Student *,Student *); //学号排序--------- 
	Student *next;   
protected:
	int bianhao; //选课编号 
	char name[20]; 
	char gender[20];
	int num;
	char major[20];
	char banji[5];
    struct score ascore[6];
	static struct schedule aschedule[10];

}*head=new Student(n,g,0,m,b);
Student::Student()                                  //默认构造函数 
{
	bianhao=0;
	cout<<"请输入学生姓名:"<<endl;
	cin>>name;
	cout<<"请输入学生性别:" <<endl;
	cin>>gender;
	while(1)
{
	cout<<"请输入学生学号:" <<endl;
	while( !(cin>>num) || cin.peek()!='\n' )
	{
	   cin.clear();
	   cin.ignore( numeric_limits<streamsize>::max(), '\n' ); 
	   cout << "输入数据错误,请重新输入:" << endl;
	}
		if(find(num,head)!=NULL)
	   {
			cout<<"您已录入过该学生,请检查学号是否正确"<<endl;
       }else break;
}
	cout<<"请输入学生专业:" <<endl;
	cin>>major;
	cout<<"请输入学生班级:"<<endl;
	cin>>banji;
	
	for(int i=0;i<6;i++)
	{
	    strcpy(ascore[i].subject,"C++");
	    ascore[i].fenshu=0;
	    ascore[i].gpa=0;
	    
    }
    next=NULL;
}
Student::Student(char n[20],char g[20],int number,char m[20],char b[5]):num(number)  //有参构造函数 
{
	strcpy(name,n);
	strcpy(gender,g);
	strcpy(major,m);
	strcpy(banji,b);
	for(int i=0;i<6;i++)
	{
	    strcpy(ascore[i].subject,"C++");
	    ascore[i].fenshu=0;
	    ascore[i].gpa=0;
    }
    next=NULL;
} 

这是我们的student类,有学生基本数据信息和成员函数。最后一行的head指针是全局变量,也是我们动态链表的头,录入信息即在动态链表的末端添加结点,代码如下,

void add()                                  //录入 
{
	Student *p=head;
	while(p->next !=NULL)
	{
	    p=p->next;	
	}
	p->next=new Student;
}

这一段代码很简单,先找到链表的最后一个结点,然后new一个student类的对象,同时调用student类的默认构造函数,就可以输入学生信息,最后让之前找到的最后一个结点的指针指向这个对象。因为在构造函数里已经将next设置为了null,所以不必担心出现野指针的问题。

 在这个系统里,每次操作之前,我们都要将文件中的信息读取出来;每一次对学生信息的添加删除修改等任何操作之后,我们都会将新的信息写入文件中,这就涉及到两个函数

void read_from_file()                                         //从文件中读取数据 
{
	Student *p=new Student(n,g,0,m,b);
	head=p;
	ifstream infile("si.txt");
	if(!infile)
	{
		cerr<<"open error!"<<endl;
		exit(1);
	}
	while(infile.peek()!=EOF)              //  infile.peek()!=EOF
	{
		p->next=new Student(n,g,0,m,b);
		p=p->next;
		infile>>*p;
		infile.get();
		if(infile.peek()=='\n')break;
	}
	p->next=NULL;
	infile.close(); 
}
void mergesort_2(Student **headref);
void write_to_file()                                   //将数据写入文件 
{
	mergesort_2(&head);
	Student *p;
	ofstream outfile("si.txt");           //,ofstream::binary
	if(!outfile)
	{
		cerr<<"open error"<<endl;
		abort();
	}
	p=head->next;
	while(p!=NULL)
	{
		outfile<<*p;
		p=p->next ;
	}
	outfile.close();
}

  这里有几点需要解释一下:

1.我为什么可以infile和outfile运算对象,因为我在这里用了运算符的重载

istream& operator>>(istream& input,Student& a)     //输入
{
    input>>a.name>>a.gender>>a.major>>a.banji>>a.num>>a.bianhao;
    for(int i=0;i<6;i++)
	{	
		input>>a.ascore[i].subject>>a.ascore[i].fenshu>>a.ascore[i].gpa;
	} 
	return input;
}
ostream& operator<<(ostream& output,Student& a)       //输出
{
	output<<a.name<<'\t'<<a.gender<<'\t'<<a.major<<'\t'<<a.banji<<'\t'<<a.num<<'\t'<<a.bianhao<<endl;
	for(int i=0;i<6;i++)
	{	
		output<<a.ascore[i].subject<<'\t'<<a.ascore[i].fenshu<<'\t'<<a.ascore[i].gpa<<endl;
	} 
	return output;
} 

这两个函数都在student类里面申明为友元函数,就可以对类的数据进行操作。

2.infile.get():使文件指针下移一位,infile.peek!='\n'是为了避免文件末尾的换行符被读进链表中。

3.将链表写入文件之前,我们对学号进行了排序,这样会方便日后的查找。方法是归并排序,有兴趣的同学可以自己去查一查,由于篇幅有限,我在这里就不多说了。

Student* sortedmerge_2(Student *a,Student *b)  //学号排序 
{
	Student *result=NULL;
	if(a == NULL)
		return (b);
	else if(b == NULL)
		return (a);
	if(a->num <b->num )
	{
		result=a;
		result->next =sortedmerge_2(a->next, b);
	}
	else
	{
		result=b;
		result->next =sortedmerge_2(a,b->next);
	}
	return (result);
}
void FrontBackSplit(Student *source,Student **frontref,Student **backref) 
{
	Student *fast,*slow;
	if(source == NULL || source->next == NULL)
	{
		*frontref = source;
		*backref = NULL;
	}
    else
    {
    	fast=source->next ;
    	slow=source;
    	while(fast!=NULL)
    	{
    		fast=fast->next ;
    		if(fast!=NULL)
    		{
    			fast=fast->next ;
    			slow=slow->next ;
			}
		}
		*frontref = source;
		*backref = slow->next;
		slow->next = NULL;
	}
}
void mergesort_2(Student** headref)  //void mergesort(Student** headref,int x)  排学号 
{
	Student *head=*headref;
	Student *a,*b;
	if((head == NULL) || (head->next == NULL))
	{
		return;
	}
	FrontBackSplit(head,&a,&b);
	mergesort_2(&a);
	mergesort_2(&b);
	*headref=sortedmerge_2(a,b);
}

接下来介绍功能2:插入信息

这个功能其实是在链表的指定位置插入结点

所以第一步是找到要插入位置的前一个结点,我们通过学号来进行查找,调用函数p=find(number,head); p即前一个结点

Student *find(int number,Student *p)                 //查询 
{
	int i;
	while(p!=NULL)//当p不等于null的时候就一直找下去 
	{
		if(number==p->num)//p是一个类的指针,指向类的数据成员 
		{
			return p;
		}
		p=p->next;
	}
	return NULL;
}

然后插入,调用函数p->insert()

void Student::insert()        //插入新同学 
{
	Student *p=new Student;
	p->next=this->next;
	this->next=p;
}

其实这个功能有些鸡肋,因为我们每次存入信息之前都会对学号进行一次排序。只是在这里要讲动态链表的话,实现在链表中间插入是值得一提的。

再介绍功能3:显示信息

同样,我们需要先进行查询,仍是之前的find函数,p=find(number,head);通过学号找到该同学后,p->show();

void Student::show()                                                                                //输出学生信息 
{
	cout<<"姓名:"<<setw(15)<<"性别:"<<setw(15)<<"专业班级:"<<setw(15)<<"学号:"<<endl;
	cout<<name<<setw(15)<<gender<<setw(15)<<major<<banji<<setw(15)<<num<<endl;
	cout<<"科目:"<<setw(15)<<"分数:"<<setw(15)<<"GPA:"<<endl;
	for(int i=0;i<6;i++)
	{
		
		cout<<ascore[i].subject<<setw(15)<<ascore[i].fenshu<<setw(15)<<ascore[i].gpa<<endl;
	} 

}

功能4:删除信息

先查询其前一个结点的指针p=find(num-1,head); 然后用p->remove();

void Student::remove()         //删除函数 
{
	Student *z;
	z=this->next;
	this->next=z->next;
	delete(z);
}

在这个函数里,将要删除的结点指针赋给z,然后令p的next指向z的next,因为这是动态链表,所以需要delete(z),否则会导致内存溢出。

功能5:修改信息

同样需先查询到该结点,这里就不再赘言了,修改函数代码如下

void menu2()                                                                           //修改菜单 
{
	cout<<"_______________________________________"<<endl;
	cout<<"|             修改学生信息             |"<<endl;
	cout<<"|______________________________________|"<<endl;
	cout<<"|       1.修改姓名      2.修改班级     |"<<endl; 
	cout<<"|______________________________________|"<<endl;
	cout<<"|       3.修改专业      4.修改性别     |"<<endl;
	cout<<"|______________________________________|"<<endl;
	cout<<"|       5.修改学号                     |"<<endl;
	cout<<"|______________________________________|"<<endl;
}
void Student::modify()                                     //修改 
{
	int m;
	menu2();
	cout<<"请输入您想修改的内容"<<endl;
	while( !(cin>>m) || cin.peek()!='\n' )
	{
	   cin.clear();
	   cin.ignore( numeric_limits<streamsize>::max(), '\n' ); 
	   cout << "输入数据错误,请重新输入:" << endl;
	}
	switch(m)
	{
		case 1:{
				cout<<"您想把信息修改为"<<endl;
				cin>>name;
				break;
				}
		case 2:{
				cout<<"您想把信息修改为"<<endl;
				cin>>banji;
				break;
				}
		case 3:{
				cout<<"您想把信息修改为"<<endl;
				cin>>major;
				break;
				}
		case 4:{
				cout<<"您想把信息修改为"<<endl;
				cin>>gender;
				break;
				}
		case 5:{
				cout<<"您想把信息修改为"<<endl;
				cin>>num;
				break;
				}
	}
}

我在这里做了一个容错处理,如果用户输入不合法,就会报错,不至于使程序陷入死循环。

while( !(cin>>m) || cin.peek()!='\n' )
	{
	   cin.clear();
	   cin.ignore( numeric_limits<streamsize>::max(), '\n' ); 
	   cout << "输入数据错误,请重新输入:" << endl;
	}

如果输入不合法,就先用cin.clear()重置流的状态,使之有效
如果流处于错误的状态,cin.ignore();是不会执行的,最后用cin.ignore清空cin的缓冲区。

好了,学生信息管理系统的基础版就完成了,请大家探讨指正。

  • 56
    点赞
  • 275
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值