面向对象程序设计C++:编写一个图书管理系统


一、课程设计内容:

编写一个图书管理系统对学生信息和图书信息进行管理。其中学生信息包括姓名,学号,所属学院,最大借阅量等属性,图书信息包括索取号,书名,作者,出版社,数量等属性,要求能输入、输出、修改、查询学生借阅图书的信息。

二、设计流程图或功能框图

在这里插入图片描述

三、类功能及成员设计:

本次实验构建了图书类、学生类、图书馆类三个类
1、图书类 :用于作为图书对象记录每本书的包括索取号、书名、作者、出版社、数量的5个属性:

class Book{
public:
	string index;
	string bookname;
	string author;
	string publisher;
	int quantity;
	Book(){				//空书标记 
		index = '\0';	
	}
	Book(string line)
	{
		stringstream input(line); 	//stringstream以空格为区分 
		input>>index>>bookname>>author>>publisher>>quantity; 
//line为一行,以空格区分获取5个输入,分别为索取号,书名,作者,出版社,数量 
	}
	void display()
	{
		cout<< setw(16)<<index<<" "<< 
		  	   setw(16)<<bookname<<" "<< 
			   setw(16)<<author<<" "<< 
			   setw(16)<<publisher<<" "<< 
			   setw(16)<<quantity<<endl;
	}
};

2、学生类 :用于作为学生对象记录每个学生的包括学号、姓名、学院、最大借阅量的4个属性,以及借阅图书的数量,和所有已借图书的索取号:

class Student 
{
public:
	string cardId;					//学号
	string student_name;			//姓名
	string college;					//学院
	int borrow_max;				//最大借阅数量
	vector<string> borrowed;		//设置数组,记录被借书籍的索取号 
	Student(string line)
	{
		stringstream input(line); 	//stringstream以空格为区分 
		input>>cardId>>student_name>>college>>borrow_max; //line为一行,以空格区分获取4个输入,分别为学号,姓名,学院,最大借阅数量 
		string data;
		input >> data;
		int num = atoi(data.c_str());//atoi函数:将字符串转int ,这里读取该生已借图书的数量 
		for(int i = 0; i < num; i++)
		{
			input >> data;
			borrowed.push_back(data);
		}
	}
	Student() {}
	display(){
		cout<<setw(16)<<"学生学号"<<" "<<setw(16)<<"学生姓名"<<" "<<setw(16)<<"学生学院"<<" "<<setw(16)<<"最大借阅量"<<endl;
		cout<<setw(16)<<cardId<<" "<<setw(16)<<student_name<<" "
			<<setw(16)<<college<<" "
			<<setw(16)<<borrow_max<<" "<<endl;
	} 
};

3、图书馆类 :可以理解为管理功能,创建“书柜”和“学生列表”,用于管理学生信息与图书信息,实现学生信息录入、学生信息查询、申请借阅图书、申请归还图书的学生管理功能,和图书信息录入、图书信息查询、图书信息删除、统计借阅信息的图书管理功能。

class Library 
{
public:
	//图书管理 
	vector<Book> bookshelf;			//设定数组:书柜,里面存所有的图书信息
	string bookAttribute[5] = {"图书索取号", "书名", "作者", "出版社", "图书数量"}; //书籍信息属性 
	int bookAttributeNum = 5;
	bool Book_entry();				//图书信息录入 
	bool Book_check();				//图书信息查询 
	bool Book_delete();			    //图书信息删除 
	bool Book_statistics();			//统计借阅信息 
	Book Book_search(string index); //对象为已借图书的索取号 
	//学生管理 
	vector<Student> studentList; 	//设定数组:学生列表,里面存所有的学生信息 
	string studentAttribute[4] = {"学号", "姓名", "学院", "最大借阅量"}; //学生信息属性 
	int studentAttributeNum = 4;
	bool Student_entry();			//学生信息录入 
	bool Student_check();			//学生信息查询 
	bool Student_borrow();			//申请借阅图书 
	bool Student_return();			//申请归还图书 
	//公共函数 
	Library();						//构造函数:做初始化,将文本已有数据读进内存
	~Library();						//析构函数
};

四、设计方法描述:

设计思路:运行程序输出显示图书馆管理系统的功能选项,根据提示输入相应序号0~8选择不同的系统功能,包括管理学生信息的4个模块,以及管理图书信息的4个模块,输入“0”退出系统。当输入指令不正确时提示重新输入,输入正确则执行对应功能的子函数,执行函数时若操作或输入不正确会返回显示对应的错误提示,每次执行完毕后会重新显示选择功能选项,直至退出系统或关闭控制台窗口。

不同功能的主要程序展示(仅截取关键部分):

(1)学生信息录入:

键盘输入学生的学号、姓名、学院、最大阅读量,以空格隔开,程序读取整行并录入追加到studentList中,程序结束时将本次操作录入的所有学生信息更新写入"图书馆的学生信息.txt"文本中。
Library的参数设定:

	vector<Student> studentList; 	//设定数组:学生列表,里面存所有的学生信息 
	string studentAttribute[4] = {"学号", "姓名", "学院", "最大借阅量"}; //学生信息属性 
	int studentAttributeNum = 4;

Library的学生信息录入函数:

bool Library::Student_entry()
{
	string data;
	for(int i = 0; i < studentAttributeNum; i++)
		cout << studentAttribute[i] << " ";
	cout << endl << "请输入学生的信息(以空格隔开):"; 
	fflush(stdin);		//清空输入缓冲区,为了确保不影响后面的数据读取(此处因为会读取回车键导致后续录入有细小偏差,加上此函数可解决)
	getline(cin, data);
	Student stu(data);
	studentList.push_back(stu);	//新的学生信息追加到“学生列表”数组的内存中
}

Library的析构函数(学生部分):

fstream out;
out.open("图书馆的学生信息.txt", ios::out);
	for(int i = 0; i < studentList.size(); i++)
	{ 
		out << studentList[i].cardId << " " << studentList[i].student_name << " " << studentList[i].college << " " << studentList[i].borrow_max << " "<< studentList[i].borrowed.size() << " ";
		for(int j=0;j<studentList[i].borrowed.size();j++)
			out<<studentList[i].borrowed[j]<<" ";
		out << endl;
	} 
	out.close();

(2)学生信息查询:

读取内存studentList并循环,每个学生信息输出显示学号、姓名、学院、最大借阅量的4个属性,其中studentList为读取"图书馆的学生信息.txt"的文本信息以及本次运行操作中追加的新学生信息。
Library的学生信息查询函数:

bool Library::Student_check()
{
	cout<<"-------------------------------------------------------------------------------"<<endl;
	cout<<setw(16)<<"学生学号"<<" "<<setw(16)<<"学生姓名"<<" "<<setw(16)<<"学生学院"<<" "<<setw(16)<<"最大借阅量"<<endl;
	for(int i = 0; i < studentList.size(); i++)
		studentList[i].display();
	cout<<"-------------------------------------------------------------------------------"<<endl;
	return true;
}

Student的display函数:

	display()
	{
		cout<<setw(16)<<cardId<<" "
			<<setw(16)<<student_name<<" "
			<<setw(16)<<college<<" "
			<<setw(16)<<borrow_max<<" "<<endl;
	}

(3)申请借阅图书:

键盘输入学生的学号以及待借图书的索取号,用studentPos,bookPos作为判断标志,,置为-1,当输入学生学号或图书索取号输入有误时,将返回申请借阅失败。第一次判断条件:①输入学号在录入学生信息中存在、②该生的已借图书数量小于最大借阅量,若不满足则表明学生信息不符合,不修改studentPos的值。第二次判断条件:①输入索取号在录入图书信息中存在、②该图书在馆内数量大于0,若不满足则表明图书信息不符合,不修改bookPos的值。当两个标志值均不为-1,才为借书成功,此时将借书信息追加到学生信息studentList中,馆内对应图书数量-1,否则为申请借阅失败。
Library的申请借阅图书函数:

bool Library::Student_borrow()
{
	string inputId,inputIndex;
	int studentPos = -1, bookPos = -1;  //用studentPos,bookPos作为判断标志
	cout<<"请输入学生的学号 与 待借图书的索取号(以空格隔开):"<<endl; 
	cin >> inputId >> inputIndex;
	for(int i = 0; i < studentList.size(); i++)
//判断条件:1、输入学号在录入学生信息中存在;2、 该生的已借图书数量小于最大借阅量
		if(studentList[i].cardId == inputId && studentList[i].borrowed.size() < studentList[i].borrow_max) 
		{
			studentPos = i;
			break;
		}
	for(int i = 0; i < bookshelf.size(); i++)
	//判断条件:1、输入索取号在录入图书信息中存在;2、 该图书在馆内数量大于0 
		if(bookshelf[i].index == inputIndex && bookshelf[i].quantity > 0)
		{
			bookPos = i;
			break;
		}
	if(studentPos == -1 || bookPos == -1)
		return false;
	studentList[studentPos].borrowed.push_back(bookshelf[bookPos].index);
	bookshelf[bookPos].quantity--;
	return true;
}

(4)申请归还图书:

键盘输入学生的学号以及待还图书的索取号,与申请借阅功能不同,除了studentPos,bookPos,还需要加myBookshelfPos作为标志。因为如果只用studentPos,bookPos作为判断标志,会出现学生信息和图书信息皆存在时,学生归还非本人所借的书依然显示借书成功。针对这个情况,使用myBookshelfPos可增加对本人是否已借该书作出多一重判断。当三个标志值均不为-1,才为借书成功,此时将对学生信息studentList中的借阅本图书信息删除,馆内对应图书数量+1,否则为申请归还失败。
Library的申请归还图书函数:

bool Library::Student_return()
{
	string inputId,inputIndex;
	int studentPos = -1, bookPos = -1, myBookshelfPos = -1;
	cout<<"请输入学生的学号 与 待还图书的索取号(以空格隔开):"<<endl;
	cin >> inputId >> inputIndex;
	for(int i = 0; i < studentList.size(); i++)
		if(studentList[i].cardId == inputId)
		{
			studentPos = i;
			break;
		}
	for(int i = 0; i < studentList[studentPos].borrowed.size(); i++)
	{
		if(studentList[studentPos].borrowed[i] == inputIndex)
		{
			myBookshelfPos = i;
			break;
		}
	}
	for(int i = 0; i < bookshelf.size(); i++)
		if(bookshelf[i].index == inputIndex)
		{
			bookPos = i;
			break;
		}
	if(studentPos == -1 || bookPos == -1 || myBookshelfPos == -1)
		return false;
	studentList[studentPos].borrowed.erase(studentList[studentPos].borrowed.begin() + myBookshelfPos);
	bookshelf[bookPos].quantity++;
	return true;
}

(5)图书信息录入:

键盘输入图书的索取号、书名、作者、出版社、数量,以空格隔开,程序读取整行并录入追加到bookshelf中,程序结束时将本次操作录入的所有图书信息更新写入"图书馆的图书信息.txt"文本中。
Library的参数设定:

vector<Book> bookshelf;			//设定数组:书柜,里面存所有的图书信息
	string bookAttribute[5] = {"图书索取号", "书名", "作者", "出版社", "图书数量"}; //图书属性 
	int bookAttributeNum = 5;
Library的图书信息录入函数:
bool Library::Book_entry(){
	string data;
	for(int i = 0; i < bookAttributeNum; i++)
		cout << bookAttribute[i] << " ";
	cout << endl << "请输入图书的信息(以空格隔开):"; 
	fflush(stdin);				//清空输入缓冲区
	getline(cin, data);
	Book b(data);
	bookshelf.push_back(b);		//新的图书信息追加到"书柜"数组的内存中
	return true;
}

Library的析构函数(图书部分):

fstream out;
	out.open("图书馆的图书信息.txt", ios::out);
	for(int i = 0; i < bookshelf.size(); i++)
		out << bookshelf[i].index << " " << bookshelf[i].bookname << " " << bookshelf[i].author << " " << bookshelf[i].publisher << " " << bookshelf[i].quantity << '\n';
	out.close();

(6)图书信息查询:

读取内存bookshelf并循环,每本图书信息输出显示索取号、书名、作者、出版社、数量的5个属性,其中bookshelf为读取"图书馆的图书信息.txt"的文本信息。
Library的图书信息查询函数:

bool Library::Book_check()
{
	fstream in;
	string line;				//用于存放分割后的字符串
	vector<string> message;		//创建空类型为string的message
	//setw(int n)用来控制输出间隔,默认为空格 
	cout<<"---------------------------------------------------------------------------------------------"<<endl; 
	cout << setw(16) << "索取号"<<" "<<setw(16)<<"书名"<<" "<<setw(16)<<"作者"<<" "<<setw(16)<<"出版社"<<" "<<setw(16)<<"数量"<<endl;
	for(int i=0;i<bookshelf.size();i++)		//获取整个数组长度,每n个为一块 
		bookshelf[i].display(); 
	cout<<"---------------------------------------------------------------------------------------------"<<endl; 
	in.close();
	return true;
}

Book的display函数:

	void display()
	{
		cout<< setw(16)<<index<<" "<< 
		  	   setw(16)<<bookname<<" "<< 
			   setw(16)<<author<<" "<< 
			   setw(16)<<publisher<<" "<< 
			   setw(16)<<quantity<<endl;
	}

(7)图书信息删除:

键盘输入待删除的图书的索取号,对bookshelf数组的每本书的索取号进行遍历判断,若输入的索取号在图书中存在,则删除该书的所有信息,否则返回删除失败。
Library的图书信息删除函数:

bool Library::Book_delete()
{
	string del_book; 
	cout<<"请输入待删除图书的索取号"<<endl;
	cin>>del_book;
	int flag = true;
	for(int i=0;i<bookshelf.size();i++)		//获取整个数组长度,每n个为一块 
		if(bookshelf[i].index == del_book)
{
			bookshelf.erase(bookshelf.begin() + i);
			flag = false;
			i--;	 //不将i-1将会使遍历时产生一些漏洞,后续总结将详细说明
		}
	if(flag)
		return false;
	return true;
}

(8)统计借阅信息:

遍历studentList输出学生的学号、姓名,然后遍历该生的借书信息,borrowed记录学生借书的索取号,Book_search为图书类的对象,遍历borrowed输出该生借书的书名。当学生借阅信息不存在,将不能对应书柜信息的索取号,则返回空书,即该生没有借书。
Library的统计借阅信息函数:

bool Library::Book_statistics()
{
	cout<<setw(16)<<"学生学号"<<" "<<setw(16)<<"学生姓名"<<" "<<setw(16)<<"借书信息"<<endl << setw(8);
	for(int i = 0; i < studentList.size(); i++)
	{	
	   cout<<setw(16)<<studentList[i].cardId<<" "<<setw(16)<<studentList[i].student_name<<" ";
		for(int j = 0; j < studentList[i].borrowed.size(); j++)
			cout << Book_search(studentList[i].borrowed[j]).bookname << ", ";
		cout << endl;
	}
	return true;
}

Book的Book_search对象:

Book Library::Book_search(string index)
{
	for(int i = 0; i < bookshelf.size(); i++)
	{
		if(bookshelf[i].index == index)  	//学生借阅图书的索取号对应到书柜的索取号,
			return bookshelf[i];			//则返回书柜的该搜索行信息。 
	}
	return Book(); 					//没有匹配则返回空书,即该生没有借书 
}

(9)退出系统:

输入“0”退出系统。
补充:程序运行开始时,构造函数做初始化,将两个文本信息已有数据分别写入内存;程序结束时,析构函数:程序结束时将内存的图书信息与学生信息更新到文本里面。
Library的构造函数:

Library::Library()
{	 
	fstream in;
	in.open("图书馆的图书信息.txt",ios::in);
	if(!in)
		return;
	string line;
	while(getline(in,line))		//取文本的一行存放在line 
	{
		Book b(line);
		bookshelf.push_back(b);
	}
	in.close();
	in.open("图书馆的学生信息.txt",ios::in);//ios::app追加 
	if(!in)
		return; 
	while(getline(in,line))		//取文本的一行存放在line 
	{
		Student stu(line);
		studentList.push_back(stu);
	}
	in.close();
}

Library的析构函数:

Library::~Library()
{
	fstream out;
	out.open("图书馆的图书信息.txt", ios::out);
	for(int i = 0; i < bookshelf.size(); i++)
		out << bookshelf[i].index << " " << bookshelf[i].bookname << " " << bookshelf[i].author << " " << bookshelf[i].publisher << " " << bookshelf[i].quantity << '\n';
	out.close();
	
	out.open("图书馆的学生信息.txt", ios::out);
	for(int i = 0; i < studentList.size(); i++)
	{ 
out << studentList[i].cardId << " " << studentList[i].student_name << " " << studentList[i].college << " " << studentList[i].borrow_max << " "<< studentList[i].borrowed.size() << " ";
		for(int j=0;j<studentList[i].borrowed.size();j++)
			out<<studentList[i].borrowed[j]<<" ";
		out << endl;
	} 
	out.close();
}

五、测试用例及结果展示

(1)学生信息的录入与查询:

操作:根据提示功能输入“1”,键盘输入学生信息(学号 姓名 学院 最大借阅量),例如输入“2018123456 chenxiaohuang Law 3”,返回录入成功,则将学生信息写入内存。可通过同样方式输入另外两个学生信息,再根据功能输入“2”,返回查询成功,即可查询学生信息,相关结果展示如下:
在这里插入图片描述

(2)图书信息的录入与查询:

操作:根据提示功能输入“5”,键盘输入图书信息(索取号 书名 作者 出版社 数量),例如输入“1002 《c++》 Jane Oriental 2”,返回录入成功,则将图书信息写入内存。可通过同样方式输入其他图书信息,再根据功能输入“6”,返回查询成功,即可查询图书信息,相关结果展示如下:
在这里插入图片描述

补充:图书信息与学生信息均可通过直接修改"图书馆的学生信息.txt"、"图书馆的图书信息.txt"两个文本内容的方式实现信息录入,保存文本文件后使用查询功能即可获取相关的学生信息或图书信息。

(3)申请借阅图书:

操作:选择功能输入“3”,根据提示输入学号和索取号,返回申请借阅成功,此时查询图书信息,可以发现对应图书数量-1。相关结果展示如下:
示例①:输入“2018132058 1002”,可以查询索取号为1002的图书数量-1

在这里插入图片描述
示例②:输入“2018123456 1000”,可以查询索取号为1000的图书数量-1
在这里插入图片描述
示例③:当输入错误的学号或者错误的索取号时,例如输入“0000000000 1002”、“2018132058 0000”,均会返回申请借阅失败。
在这里插入图片描述

(4)申请归还图书:

操作:选择功能输入“4”,根据提示输入学号和索取号,返回申请归还成功,此时查询图书信息,可以发现对应图书数量+1。相关结果展示如下:
示例①:输入“2018132058 1002”,可以查询索取号为1002的图书数量+1
在这里插入图片描述

示例②:输入“2018123456 1000”,可以查询索取号为1000的图书数量+1
在这里插入图片描述

示例③:当输入错误的学号或者错误的索取号时,例如输入“0000000000 1002”、“2018132058 0000”,或者当输入学号对应的该生借阅图书索取号与该生输入待还图书索取号不匹配时,例如输入“2018132058 1001”(该生借书1002,而不是1001),均会返回申请归还失败。
在这里插入图片描述

(5)图书信息删除:

操作:选择功能输入“7”,根据提示输入待删除图书的索取号,返回图书信息删除成功,此时查询图书信息,可以发现对应图书信息已被删除。相关结果展示如下:
示例①:输入“1001”,可以查询索取号为1001的图书信息已被删除
在这里插入图片描述

示例②:输入“0000”,不存在该图书信息,返回删除失败
在这里插入图片描述

(6)统计借阅信息:

操作:选择功能输入“8”,返回统计借阅信息成功,即可查看借阅信息,相关结果展示如下:
示例:根据借书功能先让学号为2018132058的学生借阅图书《Programming》与《c++》,让学号为2018123456的学生借阅图书《c++》,此时统计借阅信息,显示学生学号、学生姓名、借阅图书的书名。
在这里插入图片描述

六、总结及体会(包括设计中遇到的问题及解决方法等)

1、遇到的问题及解决方法

(1)最初设计思路是将数据信息存入文本,每次操作即时修改文本信息,但当操作借书、还书等需要修改文本数量类型的数据的功能时,会遇到从文本取出写入数据需要频繁将字符型强制转换为int型,且即时调用与修改文本不符合大量数据操作的工程思想,解决方法为程序开始时通过构造函数将文本已有信息写入内存,本次程序运行结束后通过析构函数将本次运行的所有操作结果更新到文本里,同时解决了变量类型不符操作条件需要频繁转换的问题。
(2)实现图书信息删除功能时,由于erase是删除信息后下一行自动向前补位,待删除书被删除后,该书的下一本将自动向前补位,从而躲过本轮识别判断,一般不会对结果产生影响,但在极小部分情况下会对结果产生影响:比如待删除图书信息存在重复且为相邻排列的情况。解决方法为在循环遍历中需要添加i–语句,使删除本行信息后i倒退一行,从而可以对补位行进行识别判断。
for(int i=0;i<bookshelf.size();i++) //获取整个数组长度,每n个为一块
if(bookshelf[i].index == del_book){
bookshelf.erase(bookshelf.begin() + i);
flag = false;
i–; //不将i-1将会使遍历时产生一些漏洞
}
(3)实现申请归还图书功能时,只用studentPos,bookPos作为判断标志,会出现学生信息和图书信息皆存在时,学生归还非本人所借的书依然显示借书成功,例如学生A仅借阅图书①,而馆内存在图书①与图书②,当学生A申请归还图书②会发生依然归还成功的情况。针对这个情况,解决方法为增加myBookshelfPos作为判断标志可增加对本人是否已借该书作出多一重判断。当三个标志值均不为-1,才为借书成功。
(4)在读取文本数据过程中,fstream类读取文本文档出现中文乱码问题,根据查阅资料,出现中文乱码问题可能出自需要调节文本输出类型为UTF-8,或编程软件版本原因,修改输出类型后可以成功录入与输出中文。

2、实验程序优化

(1)以录入图书信息为例,录入信息时由于属性较多,编写程序需要频繁使用cout、cin、out,用包含空格的字符串输入可以简化代码,且在利用数组调用内存时有更直观的思路。
优化前:
cout<<“请输入图书的索引号”<<endl;
cin>>booknum;
out<<booknum<<" “;
cout<<“请输入图书的书名”<<endl;
cin>>bookname;
out<<bookname<<” “;
cout<<“请输入图书的作者”<<endl;
cin>>author;
out<<author<<” “;
cout<<“请输入图书的出版社”<<endl;
cin>>publish;
out<<publish<<” “;
cout<<“请输入图书的数量”<<endl;
cin>>quantity;
out<<quantity<<”\n";
优化后:string bookAttribute[5] = {“图书索取号”, “书名”, “作者”, “出版社”, “图书数量”};
int bookAttributeNum = 5;
for(int i = 0; i < bookAttributeNum; i++)
cout << bookAttribute[i] << " “;
cout << endl << “请输入图书的信息(以空格隔开):”;
fflush(stdin); //清空输入缓冲区
getline(cin, data);
Book b(data);
bookshelf.push_back(b); //新的图书信息追加到"书柜"数组的内存中
(2)以查询图书信息为例,查询信息时由于属性较多,可以利用二维数组简化输出,并且需要增加输出属性时,只用添加属性与属性种数即可,不用频繁地使用cout与调节排版。
优化后:
for(int i=0;i<bookshelf.size();i++) //获取整个数组长度,每n个为一块
bookshelf[i].display();
void display()
{
cout<< setw(16)<<index<<” “<<
setw(16)<<bookname<<” “<<
setw(16)<<author<<” “<<
setw(16)<<publisher<<” "<<
setw(16)<<quantity<<endl;
}
(3)由于设计程序在每次控制台窗口操作结束后会在析构函数将内存的图书信息与学生信息更新到文本里面,但如果没有输出“0”退出程序,而是直接点击×,将会视为强制结束程序,更新的图书信息与学生信息将无法更新到文本里面。 根据搜索资料,对程序做出以下改进: 回调函数,当用户关闭控制台时,系统会向附加到控制台的所有进程发送信号。控制台进程使用此函数来处理由进程接收的控制信号。 收到信号后,系统会在进程中创建一个新线程来执行该函数。
回调函数:
BOOL WINAPI HandlerRoutine(In DWORD dwCtrlType)
{
if(CTRL_CLOSE_EVENT ==dwCtrlType)
lib.~Library();
}


七、完整代码

/*3.请编写一个图书管理系统对学生信息和图书信息进行管理。其中学生信息包括姓名,学号,所属学院,最大借阅量等属性,
图书信息包括索取号,书名,作者,出版社,数量等属性,要求能输入、输出、修改、查询学生借阅图书的信息。*/
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <windows.h>
using namespace std;

//构建图书类 
class Book{
public:
	string index;
	string bookname;
	string author;
	string publisher;
	int quantity;
	Book(){
		index = '\0';	//空书标记 
	}
	Book(string line)
	{
		stringstream input(line); 	//stringstream以空格为区分 
		input>>index>>bookname>>author>>publisher>>quantity; //line为一行,以空格区分获取5个输入,分别为索取号,书名,作者,出版社,数量 
	}
	void display()
	{
		cout<< setw(16)<<index<<" "<< 
		  	   setw(16)<<bookname<<" "<< 
			   setw(16)<<author<<" "<< 
			   setw(16)<<publisher<<" "<< 
			   setw(16)<<quantity<<endl;
	}
};

//构建学生类
class Student 
{
public:
	string cardId;					//学号
	string student_name;			//姓名
	string college;					//学院
	int borrow_max;					//最大借阅数量
	vector<string> borrowed;		//设置数组,记录被借书籍的索取号 
	Student(string line)
	{
		stringstream input(line); 	//stringstream以空格为区分 
		input>>cardId>>student_name>>college>>borrow_max; //line为一行,以空格区分获取4个输入,分别为学号,姓名,学院,最大借阅数量 
		string data;
		input >> data;
		int num = atoi(data.c_str());//atoi函数:将字符串转int ,这里读取该生已借图书的数量 
		for(int i = 0; i < num; i++)
		{
			input >> data;
			borrowed.push_back(data);
		}
	}
	Student() {}
	
	display()
	{
		//cout<<setw(16)<<"学生学号"<<" "<<setw(16)<<"学生姓名"<<" "<<setw(16)<<"学生学院"<<" "<<setw(16)<<"最大借阅量"<<endl;
		cout<<setw(16)<<cardId<<" "
			<<setw(16)<<student_name<<" "
			<<setw(16)<<college<<" "
			<<setw(16)<<borrow_max<<" "<<endl;

//构建图书馆类
class Library 
{
public:
	//图书管理 
	vector<Book> bookshelf;			//设定数组:书柜,里面存所有的图书信息
	string bookAttribute[5] = {"图书索取号", "书名", "作者", "出版社", "图书数量"}; //书籍信息属性 
	int bookAttributeNum = 5;
	bool Book_entry();				//图书信息录入 
	bool Book_check();				//图书信息查询 
	bool Book_delete();				//图书信息删除 
	bool Book_statistics();			//统计借阅信息 
	Book Book_search(string index); //对象为已借图书的索取号 
	//学生管理 
	vector<Student> studentList; 	//设定数组:学生列表,里面存所有的学生信息 
	string studentAttribute[4] = {"学号", "姓名", "学院", "最大借阅量"}; //学生信息属性 
	int studentAttributeNum = 4;
	bool Student_entry();			//学生信息录入 
	bool Student_check();			//学生信息查询 
	bool Student_borrow();			//申请借阅图书 
	bool Student_return();			//申请归还图书 
	
	//公共函数 
	Library();
	~Library();
};

//构造函数:做初始化,将文本已有数据读进内存
Library::Library()
{	 
	fstream in;
	in.open("图书馆的图书信息.txt",ios::in);
	if(!in)
		return;
	string line;
	while(getline(in,line))		//取文本的一行存放在line 
	{
		Book b(line);
		bookshelf.push_back(b);
	}
	in.close();
	
	in.open("图书馆的学生信息.txt",ios::in);//ios::app追加 
	if(!in)
		return; 
	while(getline(in,line))		//取文本的一行存放在line 
	{
		Student stu(line);
		studentList.push_back(stu);
	}
	in.close();
}
/*析构函数:程序结束时将内存的图书信息与学生信息更新到文本里面 
图书馆的图书信息.txt的每行记录含义:索取号 书名 作者 出版社 数量
图书馆的学生信息.txt的每行记录含义:学号 姓名 学院 最大借阅量 已借图书数量 已借图书书名 */ 
Library::~Library()
{
	fstream out;
	out.open("图书馆的图书信息.txt", ios::out);
	for(int i = 0; i < bookshelf.size(); i++)
		out << bookshelf[i].index << " " << bookshelf[i].bookname << " " << bookshelf[i].author << " " << bookshelf[i].publisher << " " << bookshelf[i].quantity << '\n';
	out.close();
	
	out.open("图书馆的学生信息.txt", ios::out);
	for(int i = 0; i < studentList.size(); i++)
	{ 
		out << studentList[i].cardId << " " << studentList[i].student_name << " " << studentList[i].college << " " << studentList[i].borrow_max << " "<< studentList[i].borrowed.size() << " ";
		for(int j=0;j<studentList[i].borrowed.size();j++)
			out<<studentList[i].borrowed[j]<<" ";
		out << endl;
	} 
	out.close();
}

/* 图书管理 */
//图书信息录入 
bool Library::Book_entry(){
	string data;
	for(int i = 0; i < bookAttributeNum; i++)
		cout << bookAttribute[i] << " ";
	cout << endl << "请输入图书的信息(以空格隔开):"; 
	fflush(stdin);				//清空输入缓冲区
	getline(cin, data);
	Book b(data);
	bookshelf.push_back(b);		//新的图书信息追加到"书柜"数组的内存中
	return true;
} 

//图书信息查询
bool Library::Book_check()
{
	fstream in;
	string line;				//用于存放分割后的字符串
	vector<string> message;		//创建空类型为string的message
	//string temp; 				//暂存字符串
	//setw(int n)用来控制输出间隔,默认为空格 
	cout<<"---------------------------------------------------------------------------------------------"<<endl; 
	cout << setw(16) << "索取号"<<" "<<setw(16)<<"书名"<<" "<<setw(16)<<"作者"<<" "<<setw(16)<<"出版社"<<" "<<setw(16)<<"数量"<<endl;
	for(int i=0;i<bookshelf.size();i++)		//获取整个数组长度,每n个为一块 
		bookshelf[i].display();
	//OutData(message,5);//输出message 
	cout<<"---------------------------------------------------------------------------------------------"<<endl; 
	in.close();
	return true;
}

//图书信息删除
bool Library::Book_delete()
{
	string del_book; 
	cout<<"请输入待删除图书的索取号"<<endl;
	cin>>del_book;
	int flag = true;
	for(int i=0;i<bookshelf.size();i++)		//获取整个数组长度,每n个为一块 
		if(bookshelf[i].index == del_book)
		{
			bookshelf.erase(bookshelf.begin() + i);
			flag = false;
			i--;  //这里i--是因为erase是删除信息后下一行自动向前补位,不将i-1将会使遍历时产生一些漏洞 
		}
	if(flag)
		return false;
	return true;
}

//统计借阅信息
bool Library::Book_statistics()
{
	cout<<setw(16)<<"学生学号"<<" "<<setw(16)<<"学生姓名"<<" "<<setw(16)<<"借书信息"<<endl << setw(8);
	for(int i = 0; i < studentList.size(); i++)
	{	
		cout<<setw(16)<<studentList[i].cardId<<" "<<setw(16)<<studentList[i].student_name<<" ";
		for(int j = 0; j < studentList[i].borrowed.size(); j++)
			cout << Book_search(studentList[i].borrowed[j]).bookname << ", ";
		cout << endl;
	}
	return true;
} 

Book Library::Book_search(string index)
{
	for(int i = 0; i < bookshelf.size(); i++)
	{
		if(bookshelf[i].index == index)  	//学生借阅图书的索取号对应到书柜的索取号,
			return bookshelf[i];			//则返回书柜的该搜索行信息。 
	}
	return Book(); 							//没有匹配则返回空书,即该生没有借书 
}


/* 学生管理 */
//学生信息录入 
bool Library::Student_entry()
{
	string data;
	for(int i = 0; i < studentAttributeNum; i++)
		cout << studentAttribute[i] << " ";
	cout << endl << "请输入学生的信息(以空格隔开):"; 
	fflush(stdin);		//清空输入缓冲区,为了确保不影响后面的数据读取(此处因为会读取回车键导致后续录入有细小偏差,加上此函数可解决) 
	getline(cin, data);
	Student stu(data);
	studentList.push_back(stu);   //新的学生信息追加到“学生列表”数组的内存中 
}

//学生信息查询 
bool Library::Student_check()
{
	cout<<"-------------------------------------------------------------------------------"<<endl;
	cout<<setw(16)<<"学生学号"<<" "<<setw(16)<<"学生姓名"<<" "<<setw(16)<<"学生学院"<<" "<<setw(16)<<"最大借阅量"<<endl;
	for(int i = 0; i < studentList.size(); i++)
		studentList[i].display();
	cout<<"-------------------------------------------------------------------------------"<<endl;
	return true;
}

//申请借阅图书 
bool Library::Student_borrow()
{
	string inputId,inputIndex;
	//用studentPos,bookPos作为判断标志,当输入学生学号或图书索取号输入有误时,将返回申请借阅失败
	int studentPos = -1, bookPos = -1;  
	cout<<"请输入学生的学号 与 待借图书的索取号(以空格隔开):"<<endl; 
	cin >> inputId >> inputIndex;
	for(int i = 0; i < studentList.size(); i++)
		//判断条件:1、输入学号在录入学生信息中存在;2、 该生的已借图书数量小于最大借阅量
		if(studentList[i].cardId == inputId && studentList[i].borrowed.size() < studentList[i].borrow_max) 
		{
			studentPos = i;
			break;
		}
	for(int i = 0; i < bookshelf.size(); i++)
		//判断条件:1、输入索取号在录入图书信息中存在;2、 该图书在馆内数量大于0 
		if(bookshelf[i].index == inputIndex && bookshelf[i].quantity > 0)
		{
			bookPos = i;
			break;
		}
	if(studentPos == -1 || bookPos == -1)
		return false;
	studentList[studentPos].borrowed.push_back(bookshelf[bookPos].index);
	bookshelf[bookPos].quantity--;
	return true;
}
//申请归还图书
/*增加myBookshelfPos作为标志:因为如果只用studentPos,bookPos作为判断标志,会出现学生信息和图书信息皆符合时,
学生归还非本人所借的书依然显示借书成功,使用myBookshelfPos可增加对本人是否已借该书作出多一重判断 */ 
bool Library::Student_return()
{
	string inputId,inputIndex;
	int studentPos = -1, bookPos = -1, myBookshelfPos = -1;
	cout<<"请输入学生的学号 与 待还图书的索取号(以空格隔开):"<<endl; 
	cin >> inputId >> inputIndex;
	for(int i = 0; i < studentList.size(); i++)
		if(studentList[i].cardId == inputId)
		{
			studentPos = i;
			break;
		}
	for(int i = 0; i < studentList[studentPos].borrowed.size(); i++)
	{
		if(studentList[studentPos].borrowed[i] == inputIndex)
		{
			myBookshelfPos = i;
			break;
		}
	}
	for(int i = 0; i < bookshelf.size(); i++)
		if(bookshelf[i].index == inputIndex)
		{
			bookPos = i;
			break;
		}
	if(studentPos == -1 || bookPos == -1 || myBookshelfPos == -1)
		return false;
	studentList[studentPos].borrowed.erase(studentList[studentPos].borrowed.begin() + myBookshelfPos);
	bookshelf[bookPos].quantity++;
	return true;
}

Library lib; //由于后面回调函数需要使用Library类的析构函数,这里需要将lib设为全局变量。
 
/*由于设计程序在每次控制台窗口操作结束后会在析构函数将内存的图书信息与学生信息更新到文本里面,但如果没有
输出“0”退出程序,而是直接点击×,将会视为强制结束程序,更新的图书信息与学生信息将无法更新到文本里面。 
根据搜索资料,对程序做出以下改进: 
回调函数,当用户关闭控制台时,系统会向附加到控制台的所有进程发送信号。控制台进程使用此函数来处理由进程
接收的控制信号。 收到信号后,系统会在进程中创建一个新线程来执行该函数。 */ 
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType)
{
	if(CTRL_CLOSE_EVENT ==dwCtrlType)
		lib.~Library();
}


//主函数
int main()
{
	SetConsoleCtrlHandler(HandlerRoutine, TRUE);
	Student st[10];
	int order;
	do
	{
		order = -1;
		cout<<"        欢迎来到图书管理系统!\n";
		cout<<"*****************************************\n";
		cout<<"管理学生信息:\n"; 
		cout<<"| 1. 学生信息录入    2. 学生信息查询    |\n";
		cout<<"| 3. 申请借阅图书    4. 申请归还图书    |\n";
		cout<<"|			   按 \"0\"退出   |\n";
		cout<<"*****************************************\n";
		cout<<"管理图书信息:\n"; 
		cout<<"| 5. 图书信息录入    6. 图书信息查询    |\n";
		cout<<"| 7. 图书信息删除    8. 统计借阅信息    |\n";
		cout<<"|			   按 \"0\"退出   |\n";		
		cout<<"*****************************************\n";
		cout<<"      请输入1~8选择系统功能:  ";
		cin>>order;
		switch(order)
		{
			case 1:
				if(lib.Student_entry()==0)					//学生信息录入 
					cout<<"学生信息录入失败!"<<endl<<endl;
				else
					cout<<"学生信息录入成功!"<<endl<<endl; 
				break;
			case 2:
				if(lib.Student_check()==0)					//学生信息查询
					cout<<"学生信息查询失败!"<<endl<<endl;
				else
					cout<<"学生信息查询成功!"<<endl<<endl; 
				break;
			case 3:
				if(lib.Student_borrow()==0)					//申请借阅图书 
					cout<<"申请借阅图书失败!"<<endl<<endl;
				else
					cout<<"申请借阅图书成功!"<<endl<<endl;
				break;
			case 4:
				if(lib.Student_return()==0)					//申请归还图书 
					cout<<"申请归还图书失败!"<<endl<<endl;
				else
					cout<<"申请归还图书成功!"<<endl<<endl;
				break;
			case 5:
				if(lib.Book_entry()==0)						//图书信息录入 
					cout<<"图书信息录入失败!"<<endl<<endl;
				else
					cout<<"图书信息录入成功!"<<endl<<endl;
				break;
			case 6:
				if(lib.Book_check()==0)						//图书信息查询
					cout<<"图书信息查询失败!"<<endl<<endl;
				else
					cout<<"图书信息查询成功!"<<endl<<endl;
				break;
			case 7:
				if(lib.Book_delete()==0)					//图书信息删除
					cout<<"图书信息删除失败!"<<endl<<endl;
				else
					cout<<"图书信息删除成功!"<<endl<<endl;
				break;
			case 8:
				if(lib.Book_statistics()==0)				//统计借阅信息 
					cout<<"统计借阅信息失败!"<<endl<<endl;
				else
					cout<<"统计借阅信息成功!"<<endl<<endl;
				break;
			case 0:											//退出系统 
				break;
			default:
				cout<<"命令输入错误!请重新输入!"<<endl;	 
				break;
		}	
	}while(order != 0);
	return 0;
}

  • 31
    点赞
  • 279
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,我可以为您提供一些关于C++面向对象图书管理系统的基本设计思路。 首先,我们可以考虑设计几个关键的类,例如图书类、用户类、借阅记录类等等。 对于图书类,我们可以考虑包含以下属性:书名、作者、出版社、ISBN号、出版日期、价格、总库存量、可借数量等等。同时,我们需要为图书类设计一些方法,例如添加图书、删除图书、修改图书信息等等。 对于用户类,我们可以考虑包含以下属性:用户名、用户ID、借阅历史记录等等。为用户类设计的方法可以包括用户注册、登录、查询借阅记录等等。 对于借阅记录类,我们可以考虑包含以下属性:图书ID、借书时间、预计归还时间、实际归还时间等等。为借阅记录类设计的方法可以包括添加借阅记录、查询借阅记录、归还图书等等。 在设计完类之后,我们需要考虑如何将它们组合在一起构建出一个完整的图书管理系统。我们可以考虑设计一个主控制类,该类可以包含所有的图书、用户、借阅记录等对象,并且负责协调它们之间的交互关系和业务逻辑。 最后,我们需要考虑如何设计用户界面,以便用户可以方便地使用图书管理系统。我们可以考虑使用命令行界面或者图形用户界面来实现这一点。 以上是一个简单的C++面向对象图书管理系统的设计思路,具体实现可以根据需求进行调整和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值