c++学生信息管理系统(一)

尝试重新设计与编写大一第一学期的 c++课设——学生信息管理系统。本文作简单思路分析与代码分享。B 站视频内录制了从头开始写的整个过程:课程设计|c++控制台简易学生信息管理系统

思路

要求:能够录入,显示,查找,删除,文件存取学生信息

以当时的知识是以链表来实现的,这次也是使用链表。

首先,创建一个链表结点类用于存放学生的信息,每个对象都是一个学生。

其次,创建一个链表类用于将结点连接起来。

最后,利用链表类已经创建好的各种接口,在 main 函数中进行装配,实现所需要的各种功能。

链表结点类

  • 类名:CStudent
  • 属性:姓名、性别、成绩、其余本质相同的属性(如班级号,学号)省略。
  • 方法:以不同方式显示该学生所有信息、手动录入学生信息

类声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#pragma once
//链表结点类
//学生类
//属性:姓名、性别、成绩
//方法:录入、显示
class CStudent
{
char name[20];
bool sex;//true为男,false为女
int score;
public:
//链表需要的指针域
CStudent* next;
//=======================================
public:
//构造函数
CStudent(const char p_name[], bool p_sex, int p_score);
CStudent();
//录入与显示
void input();
void show(int method);
//get
char* getName() { return name; }
bool getSex() { return sex; }
int getScore() { return score; }
//析构函数
~CStudent();
};


类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//@fileName <CStudent.cpp>
#include "CStudent.h"
#include <cstring>
#include <iostream>
using namespace std;
CStudent::CStudent(const char p_name[], bool p_sex, int p_score)
{//有参构造则自动录入信息
strcpy(name, p_name);
sex = p_sex;
score = p_score;
}
CStudent::CStudent()
{
input();//如果无参构造,则手动录入信息
}
void CStudent::input()
{
cout << "请输入学生姓名:" << endl;
cin >> name;
//如果遇到cin连续输入出错的问题,可以在每次输入后加个cin.get()
cout << "请输入学生性别(1为男,0为女):" << endl;
int isex;
cin >> isex;
sex = isex ? true : false;

cout << "请输入学生成绩:" << endl;
cin >> score;

}

void CStudent::show(int method)
{

switch (method)
{
case 0://横向显示,一行一条记录
cout << name<<"\t"
<< (sex ? "男" : "女")<<"\t"
<< score<<"\t"
<< endl;

break;
case 1://纵向显示,每行一个属性
cout << "姓名:" << name << endl
<< "性别:" << (sex ? "男" : "女") << endl
<< "成绩:" << score << endl
<< endl;
break;
default:
break;
}

}

CStudent::~CStudent()
{
}

链表类

类声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//@fileName <CStudentList.h>
#pragma once
#include "CStudent.h"
//链表类(带头结点的单向链表)
//属性:指向头结点的头指针
//方法:构造函数(手动输入)、构造函数(传入结点对象数组)、析构函数
//方法:显示表、查找、删除、把数据存入文件、从文件中读取数据

class CStudentList
{
CStudent* head;//头指针
public:
//构造函数
CStudentList(int n);//手动录入n个学生的信息
CStudentList(CStudent s[],int n);//通过对象数组自动录入n个学生的信息
CStudentList(const char fileName[]);//读取文件中的数据信息来初始化
CStudentList();
//功能
void showList();//显示整个链表的信息
int search(const char name[]);//按名字查找并返回找到的个数
void deleteNode(CStudent*p);//删除指针p指向的结点
int deleteByName(const char name[]);//删除表中第一个匹配的记录,同时返回是否删除成功
//文件读写
void save(const char fileName[]);
void open(const char fileName[]);
//析构函数
~CStudentList();
};

类实现

构造函数

我设计了四个构造函数。

1.如果没有参数,那么就只建立一个空链表,即只有一个头结点的链表。

1
2
3
4
5
6
7
//@funcName <CStudentList::CStudentList>
//@brief <创建空链表>
CStudentList::CStudentList()
{
head = new CStudent("HEAD", 1, 100);//头结点本身的数据并不重要,所以随意填写。
head->next = NULL;
}

2.手动录入信息的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//@funcName <CStudentList::CStudentList(int n)>
//@brief <创建n个结点的链表,并手动录入信息>
//@parameter <n:初始链表结点数目(不计头结点)>
CStudentList::CStudentList(int n)
{
head = new CStudent("HEAD",1,100);
head->next = NULL;
for (int i = 0; i < n; i++)
{
CStudent *newNode = new CStudent();
newNode->next = head->next;
head->next = newNode;
}
}

3.通过数组自动录入信息的构造函数

和上一个差不多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//@funcName <CStudentList::CStudentList(CStudent s[],int n)>
//@brief <创建n个结点的链表,并自动从数组中获取信息>
//@parameter <s:结点类对象数组><n:数组s的长度>
CStudentList::CStudentList(CStudent s[], int n)
{
head = new CStudent("HEAD", 1, 100);
head->next = NULL;
for (int i = 0; i < n; i++)
{
CStudent *newNode = new CStudent(s[i].getName(),s[i].getSex(),s[i].getScore());
newNode->next = head->next;
head->next = newNode;
}
}

4.通过文件自动录入信息的构造函数

使用到了另一个成员函数open()

1
2
3
4
5
6
7
8
9
//@funcName <CStudentList::CStudentList(const char fileName[])>
//@brief <自动从文件中读取信息>
//@parameter <fileName:数据来源文件的名字>
CStudentList::CStudentList(const char fileName[])
{
head = new CStudent("HEAD", 1, 100);
head->next = NULL;
open(fileName);
}

显示链表

1
2
3
4
5
6
7
8
9
10
11
12
//@funcName <CStudentList::showList()>
//@brief <显示整个链表>
void CStudentList::showList()
{
CStudent*p = head->next;
cout << "姓名\t性别\t成绩" << endl;
while (p != NULL)
{
p->show(0);//以一行一记录的形式显示
p = p->next;//工作指针向后移动
}
}

查询结点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//@funcName <CStudentList::search>
//@brief <按照名字查找数据并显示>
//@parameter <name:要查找的学生的名字>
//@return <找到的记录数目>
int CStudentList::search(const char name[])
{
int num = 0;
CStudent*p = head->next;
cout << "---------查找结果---------" << endl;
while (p != NULL)//遍历链表
{
if (strcmp(p->getName(),name)==0)//找到了需要的信息
{
num++;
p->show(0);
}
p = p->next;
}
return num;
}

查找删除

由于删除结点与查找要删除的结点相对独立,因此将删除结点独立出来一个函数,以便查找删除不同属性的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//@funcName <CStudentList::deleteNode>
//@brief <删除指针指向的链表节点>
//@parameter <p:要删除的结点的指针>
void CStudentList::deleteNode(CStudent*p)
{
CStudent*p1 = head, *p2 = head->next;
while (p2 != p)
{
p1 = p1->next;
p2 = p2->next;
}
p1->next = p->next;
delete p;
}

以查找姓名的删除函数为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//@funcName <CStudentList::deleteByName>
//@brief <按名字查找并删除第一个符合条件的结点>
//@parameter <name:要删除的结点的名字>
//@return <是否删除成功(成功返回0,失败返回-1)>
int CStudentList::deleteByName(const char name[])
{
CStudent*p = head->next;
while (p != NULL)
{
if (strcmp(p->getName(), name) == 0)//找到了需要的信息
{
deleteNode(p);
return 0;
}
p = p->next;
}
return -1;
}

读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//@funcName <CStudentList::open>
//@brief <从文件中读取数据并以覆盖形式写入链表>
//@parameter <fileName:数据文件名>
void CStudentList::open(const char fileName[])
{
//清空链表
CStudent* p = head->next;
while (p != NULL)
{
delete head;
head = p;
p = p->next;
}
//未删除head
//从文件读取数据
ifstream fin(fileName);
while (!fin.eof())//end of file
{
char name[20];
bool sex=0;
int score=0;

fin >> name >> sex >> score;

//利用头插法把数据插入到链表中
CStudent *newNode = new CStudent(name,sex,score);
newNode->next = head->next;
head->next = newNode;
}
}

保存数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//@funcName <CStudentList::save>
//@brief <将链表存入文件>
//@parameter <fileName:保存到的数据文件名>
void CStudentList::save(const char fileName[])
{
ofstream fout(fileName);//打开文件,创建文件流对象
//遍历链表
CStudent *p = head->next;
while (p != NULL)
{
//将数据存入
fout << p->getName() << " "
<< p->getSex() << " "
<< p->getScore()
<<endl;
p = p->next;
}
}

析构函数

1
2
3
4
5
6
7
8
9
10
11
CStudentList::~CStudentList()
{
CStudent* p = head->next;
while (p != NULL)
{
delete head;
head = p;
p = p->next;
}
delete head;
}

菜单

菜单比较简单,整个程序主要流程:

  1. 显示菜单选项,等待输入选项编号
  2. 分支语句,按照不同选项调用链表提供的函数
  3. 如果没有选择退出选项就循环

菜单函数示例

1
2
3
4
5
6
7
8
9
void menu()
{
cout << "========学生信息管理系统========" << endl;
cout << "1.显示学生信息表" << endl;
cout << "2.查找学生信息" << endl;
cout << "3.从文件读取" << endl;
cout << "4.将数据存入文件" << endl;
cout << "0.退出" << endl;
}

选项分支示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main()
{

int opt = -1;
CStudentList list("data.txt");
while (opt != 0)
{
menu();
cin >> opt;
switch (opt)
{
case 1:
system("cls");
list.showList();
break;
default:
break;
}

}
return 0;
}

作者

憧憬少

发布于

2019-02-27

更新于

2019-02-27

许可协议