第一章 使用的数据结构
https://gitee.com/mayonaka/LibraryManageSystem
百度云:https://pan.baidu.com/s/1G95yPyGG080b6yXcjc8B0g
提取码:4q8b
程序无非就是数据结构+算法。而数据结构就是程序中用来组织数据的方法。本次编写的图书管理系统用的是一个比较简单的数据结构:链表。
首先,先确定图书管理系统中需要存储的数据。
1. 书,书的属性有:书名,作者,类型,介绍,还有为了唯一标记一本书,给他一个id。
2. 用户:姓名,密码,还有id。
3. 用户借阅的书:人的id,书的id,借阅时间。
因为要用链表要储存这些数据,所以要从这些数据中抽象一个父类出来作为链表中的节点(总不能为它们每一个都建立一个链表吧,那样维护起来太难),我们发现,每一个数据都有一个共同的属性,那就是id,因此,建立一个只有id属性的类作为它们的基类,在链表中使用这个基类作为节点。
//基类
class NodeType
{
public:
// 通过id来判断是否是要找的节点
virtual bool Compare(int id);
int GetId();
void SetId(int id);
NodeType* GetNext();
void SetNext(NodeType* node);
protected:
int id;
NodeType* next = NULL;
};
//用户类
class UserType : public NodeType
{
public:
QString GetName();
void SetName(QString name);
QString GetPassword();
void SetPassword(QString password);
protected:
QString name;
QString password;
};
//书类
class BookType : public NodeType
{
public:
QString GetName();
void SetName(QString name);
QString GetAuthor();
void SetAuthor(QString author);
QString GetCategory();
void SetCategory(QString category);
QString GetIntroduction();
void SetIntroduction(QString introduction);
protected:
QString name;
QString author;
QString category;
QString introduction;
};
// 用户的书类
class UserBookType : public NodeType
{
public:
// 不是通过id来判断,而是bookId
virtual bool Compare(int id);
int GetBookId();
void SetBookId(int id);
int GetYear();
void SetYear(int year);
int GetMonth();
void SetMonth(int month);
int GetDay();
void SetDay(int day);
protected:
int bookId;
int year;
int month;
int day;
};
关于这几个类的实现,都是一些比较简单的操作。除了Compare方法需要讲一下。Compare方法是用来比较的函数,在本项目中的作用就是判断当前节点是否为想要找的节点。BookType类,UserType类通过id属性来判断,UserBookType类通过bookId属性来判断,因此需要重写Compare函数。
bool NodeType::Compare(int id)
{
if (this->id == id)
{
return true;
}
else
{
return false;
}
}
bool UserBookType::Compare(int id)
{
if (this->bookId == id)
{
return true;
}
else
{
return false;
}
}
下面开始建立链表,链表中储存的节点是NoteType类型,也就是BookType,UserType,UserBookType的父类,在使用到具体子类的时候可以强转到具体的子类。
LinerList的方法:
1. Init函数:从外存中(本项目是数据库)中读取数据来初始化链表。
2. Save函数:用来保存在程序中被修改的数据,通过链表头节点的id值来判断是添加还是删除:1表示添加,调用SaveAdd函数;-1表示删除,调用SaveDelete函数,
3. SaveAdd函数:把链表中的数据添加到数据库中。
4. SaveDelete函数:把链表中的数据从数据库中删掉。
5. AddNode函数:把node节点添加到链表中。
6. DeleteNode函数:把链表中节点属性id的值为id的节点删掉
LinerList的属性:
1. List:该属性是链表的头节点。
2. Length:用来记录链表长度
3. db:这是一个QSqlDatabase类型的静态变量,本项目中所有的数据都存储在同一个数据库中,为了方便之后的读入读出操作,就设置了一个静态变量来保存数据库对象,这样在初始化一次之后,就不用再重复进行初始化操作了。注:注意类中静态变量的初始化方法。要在类体外进行初始化。
链表的实现比较简单,这里大家看一下代码就行。
需要注意的一点是,在Qt中使用MySql时,要在配置文件(.pro文件)中加一句QT += sql。
#include <QtSql/QSqlDatabase>
#include "nodetype.h"
class LinerList
{
public:
LinerList();
virtual ~LinerList();
// 从文件中读取信息来初始化链表
virtual void Init();
// 清空整个链表
virtual void Clear();
// 把链表中的信息保存下来,使用第一个节点,也就是头节点的id来标记是删除还是添加
// id为1时,向文件中添加链表中的节点信息
// id为-1时,把链表中的节点信息从文件中删除
virtual void Save();
// 向文件中添加链表中的节点信息
virtual void SaveAdd();
// 从文件中把链表中的节点信息删除
virtual void SaveDelete();
// 向链表中添加一个节点,node为空时无操作
virtual void AddNode(NodeType* node);
// 从链表中删除属性为id的节点
virtual void DeleteNode(int id);
// 获得属性为id的节点,id为-1时,获得头节点,其它返回空
virtual NodeType* GetNode(int id);
protected:
static QSqlDatabase db;
NodeType* list = NULL;
int length = 0;
};
#include "linerlist.h"
//初始化数据库对象
QSqlDatabase LinerList::db = QSqlDatabase::addDatabase("QMYSQL");
LinerList::LinerList()
{
//头节点
this->list = new NodeType();
this->list->SetNext(NULL);
//初始化数据库信息
this->db.setDatabaseName("LibraryManageSystem");
this->db.setHostName("localhost");
this->db.setUserName("user");
this->db.setPort(3306);
this->db.setPassword("123456");
}
LinerList::~LinerList()
{
NodeType* preNode = this->list;
NodeType* nextNode = this->list;
while (nextNode != NULL)
{
preNode = nextNode;
nextNode = nextNode->GetNext();
delete preNode;
}
this->list = NULL;
}
void LinerList::Clear()
{
NodeType* preNode = this->list->GetNext();
NodeType* nextNode = this->list->GetNext();
while (nextNode != NULL)
{
preNode = nextNode;
nextNode = nextNode->GetNext();
delete preNode;
}
this->length = 0;
this->list->SetNext(NULL);
}
NodeType* LinerList::GetNode(int id)
{
// 当id为-1时,返回头节点
if (id == -1)
{
return this->list;
}
NodeType* node = this->list->GetNext();
while (node != NULL)
{
if (node->GetId() == id)
{
return node;
}
node = node->GetNext();
}
return NULL;
}
void LinerList::AddNode(NodeType *node)
{
if (node == NULL)
{
return;
}
node->SetNext(this->list->GetNext());
this->list->SetNext(node);
this->length++;
}
void LinerList::DeleteNode(int id)
{
NodeType* preNode = this->list;
NodeType* nextNode = this->list->GetNext();
while (nextNode != NULL)
{
if (nextNode->Compare(id))
{
preNode->SetNext(nextNode->GetNext());
delete nextNode;
this->length--;
break;
}
else
{
preNode = nextNode;
nextNode = nextNode->GetNext();
}
}
}
// LinerList只是基类,没有要初始化的信息
// 初始化的具体实现在每一个子类中实现
void LinerList::Init()
{
}
void LinerList::Save()
{
NodeType* node = this->GetNode(-1);
// 当头节点id为1时,表示把链表中的信息添加到数据库中
// 当有节点id为-1时,表示把链表中的信息从数据库中删掉
if (node->GetId() == 1)
{
this->SaveAdd();
}
else if (node->GetId() == -1)
{
this->SaveDelete();
}
}
// 在子类中具体实现
void LinerList::SaveAdd()
{
}
// 在子类中具体实现
void LinerList::SaveDelete()
{
}