目录
- 🚀前言
- 🐍项目介绍
- ☘️核心功能模块详解
-
- 💯类的设计
- [💯图书类:基类Book + 派生类NovelBook/TextBook](#💯图书类:基类Book + 派生类NovelBook/TextBook)
- [💯读者类:基类Reader + 派生类StudentReader/TeacherReader](#💯读者类:基类Reader + 派生类StudentReader/TeacherReader)
- 💯借阅记录类BorrowRecord
- 💯工具类Utils:控制台交互优化
- 💯系统核心类LibrarySystem:业务逻辑总控
- 💯主菜单交互逻辑(main.cpp)
- 🎋运行效果展示
- 🤔可优化方向
- 🎯总结与心得体会
🚀前言

大家好!我是 EnigmaCoder。
- 这是本人C++课程的期末大作业------图书管理系统,基于C++面向对象(OOP)思想开发,全程使用STL容器实现数据存储,无需数据库依赖,通过文本文件完成数据持久化,完整覆盖图书/读者的增删改查、借阅归还、报表统计等核心功能。
🐍项目介绍
本图书管理系统基于C++面向对象编程思想开发,是一套轻量化、无数据库依赖的控制台端图书馆运营管理解决方案,适用于高校C++课程实训及小型图书馆基础管理场景。系统核心功能覆盖图书全生命周期管理、读者分级管控、借阅归还流程闭环、数据持久化存储及运营数据统计五大维度,具体能力如下:
- 图书分类管理:支持小说、教材两类图书的精细化管理,可完成图书信息的新增、查询、修改、删除操作,精准存储并展示图书编号、名称、作者、专属属性(小说类型/教材适用年级)及借阅状态,删除环节内置"已借出图书禁止删除"的业务校验规则,保障数据逻辑一致性;
- 读者分级管控:采用学生、教师双维度读者体系设计,自动适配差异化借阅上限(学生3本/教师5本),支持读者信息全生命周期管理,可快速追溯指定读者的全部借阅记录;
- 借阅归还闭环:构建多维度校验机制(图书/读者存在性、图书借阅状态、读者借阅上限),借阅成功自动生成带日期戳的借阅记录,归还操作同步更新图书状态、读者借阅计数及归还日期,确保借阅流程合规可控;
- 数据持久化:基于文本文件实现数据本地存储,系统启动自动加载历史数据,退出或手动触发时完成数据备份,避免数据丢失;
- 统计分析:内置报表生成功能,可实时统计总图书量、已借出图书量、未归还记录数等核心运营数据,输出可视化统计报表。
✅ 核心亮点:
- 纯C++实现,无第三方库依赖,可直接在Dev-C++/VS编译运行;
- 严格遵循面向对象封装、继承、多态三大特性;
- 完善的容错机制(重复添加、已借出删除、非法输入校验等);
- 数据持久化(程序退出自动保存、启动自动加载本地数据);
- 分层菜单设计,交互友好,支持学生/教师差异化借阅上限。
💯核心技术栈
| 技术点 | 应用场景 |
|---|---|
| 面向对象(OOP) | 图书/读者类的封装、继承、多态 |
| STL容器(vector/map) | 数据存储(vector遍历、map快速查找) |
| 文件IO流 | 文本文件实现数据持久化 |
| 字符串处理 | CSV格式数据解析、分割 |
| 动态类型转换(dynamic_cast) | 派生类对象的类型判断 |
| 控制台交互优化 | 输入校验、清屏、暂停等 |
💯项目结构
代码按"头文件+实现文件"分离规范组织,目录结构如下:
Library Management System/
├─ Book.h // 图书基类+派生类头文件
├─ Book.cpp // 图书类实现
├─ Reader.h // 读者基类+派生类头文件
├─ Reader.cpp // 读者类实现
├─ BorrowRecord.h// 借阅记录类头文件
├─ BorrowRecord.cpp// 借阅记录类实现
├─ LibrarySystem.h// 图书馆系统核心类头文件
├─ LibrarySystem.cpp// 系统类实现
├─ Utils.h // 工具类头文件(清屏/暂停/日期等)
├─ Utils.cpp // 工具类实现
├─ main.cpp // 主菜单交互逻辑
└─ data/ // 数据文件目录(books.txt/readers.txt/records.txt)
☘️核心功能模块详解
💯类的设计
本系统通过基类抽象共性、派生类扩展特性,结合纯虚函数实现多态,是面向对象思想的核心体现。
💯图书类:基类Book + 派生类NovelBook/TextBook
功能说明 :封装所有图书的通用属性(编号、名称、作者、借阅状态),派生类扩展特有属性(小说类型、教材适用年级),通过纯虚函数showInfo()实现多态展示。
核心代码(Book.h):
cpp
#pragma once
#include"Utils.h"
// 图书基类(抽象类,纯虚函数showInfo)
class Book {
public:
Book(const string& bookId, const string& bookName, const string& author, bool isBorrow);
virtual void showInfo() = 0; // 纯虚函数,强制派生类重写
// 封装的set/get方法
void setBookName(const string& bookName);
void setAuthor(const string& author);
void setIsBorrowed(bool key);
string getBookId()const;
string getBookName()const;
string getAuthor()const;
bool getIsBorrowed()const;
~Book();
private:
string bookId; // 图书编号
string bookName; // 书名
string author; // 作者
bool isBorrowed; // 借阅状态
};
// 小说类(派生自Book)
class NovelBook :public Book {
public:
NovelBook(const string& bookId, const string& bookName, const string& author, bool isBorrow, const string& novelType);
void showInfo()override; // 重写纯虚函数
string getNovelType()const;
private:
string novelType; // 小说类型(科幻/言情等)
};
// 教材类(派生自Book)
class TextBook :public Book {
public:
TextBook(const string& bookId, const string& bookName, const string& author, bool isBorrow, const string& grade);
void showInfo()override; // 重写纯虚函数
string getGrade()const;
private:
string grade; // 适用年级
};
核心代码(Book.cpp):
cpp
#include "Book.h"
// 基类构造函数
Book::Book(const string& bookId, const string& bookName, const string& author, bool isBorrow) {
this->bookId = bookId;
this->bookName = bookName;
this->author = author;
this->isBorrowed = isBorrow;
}
// 小说类构造+重写showInfo
NovelBook::NovelBook(const string& bookId, const string& bookName, const string& author, bool isBorrowed, const string& novelType) :
Book(bookId, bookName, author, isBorrowed), novelType(novelType) {}
void NovelBook::showInfo() {
cout << "编号:" << getBookId() << endl;
cout << "书名:" << getBookName() << endl;
cout << "作者:" << getAuthor() << endl;
if (getIsBorrowed()) cout << "借阅状态:已借出" << endl;
else cout << "借阅状态:未借出" << endl;
cout << "小说类型:" << novelType << endl;
}
// 教材类构造+重写showInfo
TextBook::TextBook(const string& bookId, const string& bookName, const string& author, bool isBorrowed, const string& grade) :
Book(bookId, bookName, author, isBorrowed), grade(grade) {}
void TextBook::showInfo() {
cout << "编号:" << getBookId() << endl;
cout << "书名:" << getBookName() << endl;
cout << "作者:" << getAuthor() << endl;
if (getIsBorrowed()) cout << "借阅状态:已借出" << endl;
else cout << "借阅状态:未借出" << endl;
cout << "教材适用年级:" << grade << endl;
}
// 基类set/get方法实现
void Book::setBookName(const string& bookName) { this->bookName = bookName; }
void Book::setAuthor(const string& author) { this->author = author; }
void Book::setIsBorrowed(bool key) { this->isBorrowed = key; }
string Book::getBookId()const { return bookId; }
string Book::getBookName()const { return bookName; }
string Book::getAuthor()const { return author; }
bool Book::getIsBorrowed()const { return isBorrowed; }
Book::~Book() {}
// 派生类get方法实现
string NovelBook::getNovelType()const { return novelType; }
string TextBook::getGrade()const { return grade; }
代码讲解:
- 基类
Book用纯虚函数showInfo()抽象"展示图书信息"的行为,派生类必须重写; - 调用
showInfo()时,会自动匹配对象的实际类型(小说/教材),实现多态; - 所有属性私有化,通过
set/get方法访问,体现封装性。
💯读者类:基类Reader + 派生类StudentReader/TeacherReader
功能说明 :封装读者通用属性(编号、姓名、已借阅数),派生类扩展特有属性(学号/教师工号),通过borrowLimit()实现不同读者的借阅上限(学生3本、教师5本)。
核心代码(Reader.h):
cpp
#pragma once
#include"Utils.h"
// 读者基类(抽象类)
class Reader {
public:
Reader(const string& readerId, const string& name, int borrowCount);
virtual int borrowLimit() = 0; // 纯虚函数:借阅上限
virtual void showInfo() = 0; // 纯虚函数:展示信息
// 封装的方法
void setName(const string& name);
void BorrowCountAdd(); // 借阅数+1
void BorrowCountSub(); // 借阅数-1
string getReaderId()const;
string getName()const;
int getBorrowCount()const;
~Reader();
private:
string readerId; // 读者编号
string name; // 姓名
int borrowCount; // 已借阅数量
};
// 学生读者(派生自Reader)
class StudentReader :public Reader {
public:
StudentReader(const string& readerId, const string& name, int borrowCount, const string& studentId);
int borrowLimit()override; // 学生借阅上限:3本
void showInfo()override;
string getStudentId()const;
private:
string studentId; // 学号
};
// 教师读者(派生自Reader)
class TeacherReader :public Reader {
public:
TeacherReader(const string& readerId, const string& name, int borrowCount, const string& teacherId);
int borrowLimit()override; // 教师借阅上限:5本
void showInfo()override;
string getTeacherId()const;
private:
string teacherId; // 教师工号
};
核心代码(Reader.cpp):
cpp
#include "Reader.h"
// 基类构造函数
Reader::Reader(const string& readerId, const string& name, int borrowCount) {
this->readerId = readerId;
this->name = name;
this->borrowCount = borrowCount;
}
// 基类方法实现
void Reader::setName(const string& name) { this->name = name; }
void Reader::BorrowCountAdd() { borrowCount++; }
void Reader::BorrowCountSub() { borrowCount--; }
string Reader::getReaderId()const { return readerId; }
string Reader::getName()const { return name; }
int Reader::getBorrowCount()const { return borrowCount; }
Reader::~Reader() {}
// 学生读者实现
StudentReader::StudentReader(const string& readerId, const string& name, int borrowCount, const string& studentId) :
Reader(readerId, name, borrowCount), studentId(studentId) {}
int StudentReader::borrowLimit() { return 3; } // 学生最多借3本
void StudentReader::showInfo() {
cout << "读者编号:" << getReaderId() << endl;
cout << "姓名:" << getName() << endl;
cout << "类型:学生" << endl;
cout << "学号:" << studentId << endl;
cout << "已借阅数量:" << getBorrowCount() << "/3" << endl;
}
string StudentReader::getStudentId()const { return studentId; }
// 教师读者实现
TeacherReader::TeacherReader(const string& readerId, const string& name, int borrowCount, const string& teacherId) :
Reader(readerId, name, borrowCount), teacherId(teacherId) {}
int TeacherReader::borrowLimit() { return 5; } // 教师最多借5本
void TeacherReader::showInfo() {
cout << "读者编号:" << getReaderId() << endl;
cout << "姓名:" << getName() << endl;
cout << "类型:教师" << endl;
cout << "工号:" << teacherId << endl;
cout << "已借阅数量:" << getBorrowCount() << "/5" << endl;
}
string TeacherReader::getTeacherId()const { return teacherId; }
💯借阅记录类BorrowRecord
功能说明:记录图书借阅的核心信息(图书编号、读者编号、借阅日期、归还日期),支持"未归还"状态的标记。
核心代码(BorrowRecord.h):
cpp
#pragma once
#include"Utils.h"
class BorrowRecord {
public:
// 构造函数(未归还/已归还)
BorrowRecord(const string &bookId,const string &readerId,const string &borrowDate);
BorrowRecord(const string &bookId, const string &readerId, const string &borrowDate, const string &returnDate);
void setReturnDate(string returnDate); // 设置归还日期
// get方法
string getBookId()const;
string getReaderId()const;
string getBorrowDate()const;
string getReturnDate()const;
private:
string bookId;
string readerId;
string borrowDate;
string returnDate; // 空字符串表示未归还
};
核心代码(BorrowRecord.cpp):
cpp
#include "BorrowRecord.h"
// 未归还记录构造
BorrowRecord::BorrowRecord(const string& bookId, const string& readerId, const string& borrowDate) {
this->bookId = bookId;
this->readerId = readerId;
this->borrowDate = borrowDate;
this->returnDate = "";
}
// 已归还记录构造
BorrowRecord::BorrowRecord(const string& bookId, const string& readerId, const string& borrowDate, const string& returnDate) {
this->bookId = bookId;
this->readerId = readerId;
this->borrowDate = borrowDate;
this->returnDate = returnDate;
}
// 设置归还日期
void BorrowRecord::setReturnDate(string returnDate) { this->returnDate = returnDate; }
// get方法实现
string BorrowRecord::getBookId()const { return bookId; }
string BorrowRecord::getReaderId()const { return readerId; }
string BorrowRecord::getBorrowDate()const { return borrowDate; }
string BorrowRecord::getReturnDate()const { return returnDate; }
💯工具类Utils:控制台交互优化
功能说明:封装控制台常用工具方法(清屏、暂停、输入缓冲区清理、获取当前日期),提升交互体验。
核心代码(Utils.h):
cpp
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<algorithm>
#include<vector>
#include <limits>
#include <ctime>
#include<map>
#include<sstream>
using namespace std;
class Utils {
public:
// 清空输入缓冲区(解决cin+getline输入异常)
static void clearInputBuffer();
// 暂停控制台(按任意键继续)
static void pauseConsole();
// 获取当前日期(格式:YYYY-MM-DD)
static string getCurrentDate();
};
核心代码(Utils.cpp):
cpp
#include "Utils.h"
// 清空输入缓冲区
void Utils::clearInputBuffer() {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
// 暂停控制台
void Utils::pauseConsole() {
cout << "\n按任意键继续...";
cin.get();
}
// 获取当前日期
string Utils::getCurrentDate() {
time_t now = time(0);
tm* ltm = localtime(&now);
char date[20];
sprintf(date, "%04d-%02d-%02d", 1900 + ltm->tm_year, 1 + ltm->tm_mon, ltm->tm_mday);
return string(date);
}
💯系统核心类LibrarySystem:业务逻辑总控
LibrarySystem是系统的"大脑",整合了图书管理、读者管理、借阅归还、数据持久化四大核心功能,通过STL容器实现高效数据操作。
头文件(LibrarySystem.h)
cpp
#pragma once
#include"Utils.h"
#include"Book.h"
#include"Reader.h"
#include"BorrowRecord.h"
#include <vector>
#include <map>
#include <fstream>
#include <algorithm>
class LibrarySystem {
public:
LibrarySystem();
~LibrarySystem();
// 图书管理
void addBook(Book* book);
Book* findBook(string id);
void deleteBook(const string& bookId);
void showAllBooks()const;
// 读者管理
void addReader(Reader* reader);
Reader* findReader(string readerId);
void deleteReader(const string& readerId);
void showAllReaders()const;
// 借阅归还
bool borrowBook(string bookId, string readerId);
bool returnBook(string bookId);
void showBorrowRecordByReader(const string& readerId);
// 数据持久化
void saveData();
void loadData();
// 系统统计
int getReaderCount() const;
int getBookCount() const;
int getBorrowedBookCount() const;
// 友元函数:报表统计
friend void printReport(LibrarySystem& sys);
private:
vector<Book*> books; // 所有图书
map<string, Book*> bookMap; // 图书编号→图书对象(快速查找)
vector<Reader*> readers; // 所有读者
map<string, Reader*> readerMap;// 读者编号→读者对象(快速查找)
vector<BorrowRecord> borrowRecords; // 所有借阅记录
vector<string> split(string s, char delim); // 字符串分割工具
};
// 统计借阅数据并打印
void printReport(LibrarySystem& sys);
实现文件(LibrarySystem.cpp)
cpp
#include "LibrarySystem.h"
LibrarySystem::LibrarySystem() {
loadData();
}
LibrarySystem::~LibrarySystem() {}
//=========图书管理相关========
//添加图书
void LibrarySystem::addBook(Book* book) {
if (findBook(book->getBookId()) != nullptr) {
cout << "图书编号[" << book->getBookId() << "]已存在,添加失败" << endl;
return;
}
books.push_back(book);
bookMap[book->getBookId()] = book;
cout << "图书《" << book->getBookName() << "》添加成功!" << endl;
}
//按编号查找图书
Book* LibrarySystem::findBook(string id) {
auto it = bookMap.find(id);
if (it != bookMap.end()) {
return it->second;
}
return nullptr;
}
//删除图书
void LibrarySystem::deleteBook(const string& bookId) {
Book* book = findBook(bookId);
if (book == nullptr) {
cout << "图书编号[" << bookId << "]不存在,删除失败!" << endl;
return;
}
if (book->getIsBorrowed()) {
cout << "图书《" << book->getBookName() << "》已借出,无法删除!" << endl;
return;
}
books.erase(remove(books.begin(), books.end(), book), books.end());
bookMap.erase(bookId);
delete book;
cout << "图书编号[" << bookId << "]删除成功!" << endl;
}
//打印所有图书
void LibrarySystem::showAllBooks()const {
if (books.empty()) {
cout << "系统中暂无图书数据!" << endl;
return;
}
cout << "================所有图书信息=================" << endl;
int index = 1;
for (Book* book : books) {
cout << index << ".";
book->showInfo();
index++;
cout << endl;
}
cout << "============================================" << endl;
}
//=========读者管理相关========
//添加读者
void LibrarySystem::addReader(Reader* reader) {
if (findReader(reader->getReaderId()) != nullptr) {
cout << "读者编号【" << reader->getReaderId() << "】已存在,添加失败!" << endl;
return;
}
readers.push_back(reader);
readerMap[reader->getReaderId()] = reader;
cout << "读者【" << reader->getName() << "】添加成功!" << endl;
}
//查找读者
Reader* LibrarySystem::findReader(string readerId) {
auto it = readerMap.find(readerId);
if (it != readerMap.end()) {
return it->second;
}
return nullptr; // 未找到返回空指针
}
//删除读者
void LibrarySystem::deleteReader(const string& readerId) {
Reader* reader = findReader(readerId);
if (reader == nullptr) {
cout << "读者编号【" << readerId << "】不存在,删除失败!" << endl;
return;
}
if (reader->getBorrowCount() > 0) { // 有未归还图书,禁止删除
cout << "读者【" << reader->getName() << "】仍有未归还图书,无法删除!" << endl;
return;
}
// 从容器中移除并释放内存
readers.erase(remove(readers.begin(), readers.end(), reader), readers.end());
readerMap.erase(readerId);
delete reader;
cout << "读者编号【" << readerId << "】删除成功!" << endl;
}
//打印所有读者
void LibrarySystem::showAllReaders()const {
if (readers.empty()) {
cout << "系统中暂无读者数据!" << endl;
return;
}
cout << "\n==================== 所有读者信息 ====================" << endl;
int index = 1;
for (Reader* reader : readers) {
cout << index << ". ";
reader->showInfo();// 自动调用StudentReader/TeacherReader的showInfo
index++;
cout << endl;
}
cout << "======================================================" << endl;
}
//=========借阅归还相关=========
//借出图书
bool LibrarySystem::borrowBook(string bookId, string readerId) {
Book* book = findBook(bookId);
Reader* reader = findReader(readerId);
if (book == nullptr || reader == nullptr) {
return false;
}
if (book->getIsBorrowed()) {
return false;
}
if (reader->getBorrowCount() >= reader->borrowLimit()) {
return false;
}
book->setIsBorrowed(true);
reader->BorrowCountAdd();
string date = Utils::getCurrentDate();
borrowRecords.emplace_back(bookId, readerId, date);
return true;
}
//归还图书
bool LibrarySystem::returnBook(string bookId) {
Book* book = findBook(bookId);
if (book == nullptr) {
return false;
}
if (!book->getIsBorrowed()) {
return false;
}
string date = Utils::getCurrentDate();
for (auto &record : borrowRecords) {
if (record.getBookId() == bookId && record.getReturnDate().empty()) {
record.setReturnDate(date);
Reader* reader = findReader(record.getReaderId());
if (reader)reader->BorrowCountSub();
book->setIsBorrowed(false);
return true;
}
}
return false;
}
//通过读者Id查看借阅记录
void LibrarySystem::showBorrowRecordByReader(const string& readerId) {
bool hasRecord = false;
cout << "\n【读者编号:" << readerId << "】的借阅记录:" << endl;
for (auto& record : borrowRecords) {
if (record.getReaderId() == readerId) {
cout << "图书编号:" << record.getBookId()
<< " | 借阅日期:" << record.getBorrowDate()
<< " | 归还状态:" << (record.getReturnDate().empty() ? "未归还" : "已归还(" + record.getReturnDate() + ")") << endl;
hasRecord = true;
}
}
if (!hasRecord) {
cout << "暂无借阅记录!" << endl;
}
}
//==========系统管理相关=========
//将数据写入文件
void LibrarySystem::saveData() {
//保存图书数据
ofstream bookFile("E:\\workspace\\C++_Project\\Library Management System\\books.txt");
for (Book* book : books) {
NovelBook* novel = dynamic_cast<NovelBook*>(book);
TextBook* text = dynamic_cast<TextBook*>(book);
if (novel) {
bookFile << novel->getBookId() << "," << novel->getBookName() << "," << novel->getAuthor()
<< "," << novel->getNovelType() << "," << (novel->getIsBorrowed() ? "1" : "0") << endl;
}
else if (text) {
bookFile << text->getBookId() << "," << text->getBookName() << "," << text->getAuthor()
<< "," << text->getGrade() << "," << (text->getIsBorrowed() ? "1" : "0") << endl;
}
}
bookFile.close();
// 保存读者数据
ofstream readerFile("E:\\workspace\\C++_Project\\Library Management System\\readers.txt");
for (Reader* reader : readers) {
StudentReader* stu = dynamic_cast<StudentReader*>(reader);
TeacherReader* tea = dynamic_cast<TeacherReader*>(reader);
if (stu) {
readerFile << stu->getReaderId() << "," << stu->getName() << "," << stu->getStudentId()
<< "," << stu->getBorrowCount() << endl;
}
else if (tea) {
readerFile << tea->getReaderId() << "," << tea->getName() << "," << tea->getTeacherId()
<< "," << tea->getBorrowCount() << endl;
}
}
readerFile.close();
// 保存借阅记录
ofstream recordFile("E:\\workspace\\C++_Project\\Library Management System\\records.txt");
for (auto& record : borrowRecords) {
recordFile << record.getBookId() << "," << record.getReaderId() << "," << record.getBorrowDate() << "," << record.getReturnDate() << endl;
}
recordFile.close();
}
//从文件加载数据到系统
void LibrarySystem::loadData() {
cout << "正在加载本地数据..." << endl;
// 1. 加载图书数据(books.txt)
ifstream bookFile("E:\\workspace\\C++_Project\\Library Management System\\books.txt");
if (bookFile.is_open()) {
string line;
while (getline(bookFile, line)) {
if (line.empty()) continue; // 跳过空行
vector<string> fields = split(line, ','); // 用之前的split函数分割字段
if (fields.size() != 5) continue; // 字段数量不对,跳过无效行
string bookId = fields[0];
string bookName = fields[1];
string author = fields[2];
string typeField = fields[3]; // 小说类型/教材年级
bool isBorrowed = (fields[4]=="1"); // "1"=已借出,"0"=可借阅
// 区分图书类型:小说(类型是"科幻/言情"等)、教材(类型是"小学/初中"等)
if (typeField == "科幻" || typeField == "言情" || typeField == "悬疑") {
// 小说图书
books.push_back(new NovelBook(bookId, bookName, author, isBorrowed, typeField));
bookMap[bookId] = books.back();
}
else {
// 教材图书(适用年级)
books.push_back(new TextBook(bookId, bookName, author, isBorrowed, typeField));
bookMap[bookId] = books.back();
}
}
bookFile.close();
cout << "图书数据加载完成,共" << books.size() << "本图书" << endl;
}
else {
cout << "未找到图书数据文件(books.txt),将创建新数据" << endl;
}
// 2. 加载读者数据(readers.txt)
ifstream readerFile("E:\\workspace\\C++_Project\\Library Management System\\readers.txt");
if (readerFile.is_open()) {
string line;
while (getline(readerFile, line)) {
if (line.empty()) continue;
vector<string> fields = split(line, ',');
if (fields.size() != 4) continue;
string readerId = fields[0];
string name = fields[1];
string idField = fields[2]; // 学号/教师工号
int borrowCount = stoi(fields[3]); // 已借阅数量(字符串转整数)
// 区分读者类型:学号通常是纯数字,教师工号可能含字母(简化判断)
if (idField.find_first_not_of("0123456789") == string::npos) {
// 学生读者(学号纯数字)
readers.push_back(new StudentReader(readerId, name, borrowCount, idField));
readerMap[readerId] = readers.back();
}
else {
// 教师读者(工号含字母/特殊字符)
readers.push_back(new TeacherReader(readerId, name, borrowCount, idField));
readerMap[readerId] = readers.back();
}
}
readerFile.close();
cout << "读者数据加载完成,共" << readers.size() << "名读者" << endl;
}
else {
cout << "未找到读者数据文件(readers.txt),将创建新数据" << endl;
}
// 3. 加载借阅记录(records.txt)
ifstream recordFile("E:\\workspace\\C++_Project\\Library Management System\\records.txt");
if (recordFile.is_open()) {
string line;
while (getline(recordFile, line)) {
if (line.empty()) continue;
vector<string> fields = split(line, ',');
if (fields.size() != 3 && fields.size() != 4) continue;
string bookId = fields[0];
string readerId = fields[1];
string borrowDate = fields[2];
string returnDate = ""; // 默认空(未归还)
// 若有第4个字段,则赋值(可能为空或具体日期)
if (fields.size() == 4) {
returnDate = fields[3];
}
// 校验图书和读者是否存在(避免无效记录)
if (findBook(bookId) != nullptr && findReader(readerId) != nullptr) {
borrowRecords.emplace_back(bookId, readerId, borrowDate, returnDate);
}
}
recordFile.close();
cout << "借阅记录加载完成,共" << borrowRecords.size() << "条记录" << endl;
}
else {
cout << "未找到借阅记录文件(records.txt),将创建新数据" << endl;
}
}
//获取读者数量(注:原代码笔误,已修正)
int LibrarySystem::getReaderCount() const{
return readers.size();
}
//获取图书数量(注:原代码笔误,已修正)
int LibrarySystem::getBookCount() const{
return books.size();
}
//获取已借阅图书的数量
int LibrarySystem::getBorrowedBookCount() const{
int count = 0;
for (Book* book : books) {
if (book->getIsBorrowed())count++;
}
return count;
}
//字符串分割工具
vector<string> LibrarySystem::split(string s, char delim)
{
vector<string> result; // 存储分割后的字段
stringstream ss(s); // 将字符串转为字符串流,方便按分隔符读取
string field; // 临时存储单个字段
// 循环读取字符串流,每次按delim分割出一个字段
while (getline(ss, field, delim)) {
result.push_back(field); // 将分割后的字段加入结果数组
}
return result;
}
//===========友元函数============
//统计借阅数据并打印
void printReport(LibrarySystem& sys) {
int totalBooks = sys.books.size();
int borrowedBooks = sys.getBorrowedBookCount();
int totalReaders = sys.readers.size();
int overdueCount = 0; // 简化版:未归还视为逾期(可根据需求优化)
// 统计未归还记录数
for (auto& record : sys.borrowRecords) {
if (record.getReturnDate().empty()) overdueCount++;
}
// 打印报表
cout << "==================== 图书馆借阅报表 ====================" << endl;
cout << "系统统计:" << endl;
cout << " - 总图书数:" << totalBooks << endl;
cout << " - 已借出图书数:" << borrowedBooks << "(占比:" << (totalBooks == 0 ? 0 : (borrowedBooks * 100.0 / totalBooks)) << "%)" << endl;
cout << " - 总读者数:" << totalReaders << endl;
cout << " - 未归还记录数:" << overdueCount << endl;
cout << "========================================================" << endl;
}
代码讲解:
- 数据存储:
vector用于遍历展示,map用于快速查找,兼顾易用性和效率; - 容错逻辑:添加/删除操作前校验"是否存在""状态是否合法",避免程序异常;
- 数据持久化:按CSV格式读写文本文件,
dynamic_cast判断派生类类型,保证数据准确性; - 笔误修正:原代码中
getReaderCount/getBookCount返回值写反,已修正。
💯主菜单交互逻辑(main.cpp)
采用分层菜单设计(主菜单→子菜单),支持图书管理、读者管理、借阅归还、系统管理四大模块,输入校验避免非法操作。
核心代码(main.cpp):
cpp
#include "LibrarySystem.h"
#include "Utils.h"
// ===================== 图书管理子菜单=====================
void bookManageMenu(LibrarySystem& sys) {
int choice = 0;
string bookId, bookName, author, novelType, grade;
Book* book = nullptr;
while (true) {
system("cls");
cout << "================ 图书管理子菜单 ================" << endl;
cout << "1. 添加小说图书" << endl;
cout << "2. 添加教材图书" << endl;
cout << "3. 修改图书信息" << endl;
cout << "4. 删除图书" << endl;
cout << "5. 按编号查找图书" << endl;
cout << "6. 查看所有图书" << endl;
cout << "0. 返回主菜单" << endl;
cout << "================================================" << endl;
cout << "请输入操作选项:";
while (!(cin >> choice)) {
Utils::clearInputBuffer();
cout << "输入无效!请输入数字:";
}
Utils::clearInputBuffer();
switch (choice) {
case 1: // 添加小说图书
cout << "\n----- 新增小说图书 -----" << endl;
cout << "图书编号:"; getline(cin, bookId);
cout << "图书名称:"; getline(cin, bookName);
cout << "作者:"; getline(cin, author);
cout << "小说类型(科幻/言情/悬疑):"; getline(cin, novelType);
sys.addBook(new NovelBook(bookId, bookName, author, false, novelType));
cout << "小说图书添加成功!" << endl;
Utils::pauseConsole();
break;
case 2: // 添加教材图书
cout << "\n----- 新增教材图书 -----" << endl;
cout << "图书编号:"; getline(cin, bookId);
cout << "图书名称:"; getline(cin, bookName);
cout << "作者:"; getline(cin, author);
cout << "适用年级:"; getline(cin, grade);
sys.addBook(new TextBook(bookId, bookName, author, false, grade));
cout << "教材图书添加成功!" << endl;
Utils::pauseConsole();
break;
case 3: // 修改图书信息
cout << "\n----- 修改图书信息 -----" << endl;
cout << "请输入要修改的图书编号:"; getline(cin, bookId);
book = sys.findBook(bookId);
if (!book) {
cout << "图书编号不存在!" << endl;
Utils::pauseConsole();
break;
}
cout << "当前图书名称:" << book->getBookName() << ",请输入新名称(直接回车则不修改):";
getline(cin, bookName);
if (!bookName.empty()) book->setBookName(bookName);
cout << "当前作者:" << book->getAuthor() << ",请输入新作者(直接回车则不修改):";
getline(cin, author);
if (!author.empty()) book->setAuthor(author);
cout << "图书信息修改成功!" << endl;
Utils::pauseConsole();
break;
case 4: // 删除图书
cout << "\n----- 删除图书 -----" << endl;
cout << "请输入要删除的图书编号:"; getline(cin, bookId);
sys.deleteBook(bookId);
Utils::pauseConsole();
break;
case 5: // 按编号查找图书
cout << "\n----- 查找图书 -----" << endl;
cout << "请输入图书编号:"; getline(cin, bookId);
book = sys.findBook(bookId);
if (book) {
cout << "\n【图书信息】" << endl;
if (NovelBook* novel = dynamic_cast<NovelBook*>(book)) novel->showInfo();
else if (TextBook* text = dynamic_cast<TextBook*>(book)) text->showInfo();
}
else {
cout << "未找到该图书!" << endl;
}
Utils::pauseConsole();
break;
case 6: // 查看所有图书
cout << "\n----- 全量图书列表 -----" << endl;
sys.showAllBooks();
Utils::pauseConsole();
break;
case 0: // 返回主菜单
return;
default:
cout << "无效选项!请重新输入。" << endl;
Utils::pauseConsole();
break;
}
}
}
// ===================== 读者管理子菜单(完整版)=====================
void readerManageMenu(LibrarySystem& sys) {
int choice = 0;
string readerId, name, studentId, teacherId;
Reader* reader = nullptr;
while (true) {
system("cls");
cout << "================ 读者管理子菜单 ================" << endl;
cout << "1. 添加学生读者" << endl;
cout << "2. 添加教师读者" << endl;
cout << "3. 修改读者信息" << endl;
cout << "4. 删除读者" << endl;
cout << "5. 按编号查找读者" << endl;
cout << "6. 查看所有读者" << endl;
cout << "0. 返回主菜单" << endl;
cout << "================================================" << endl;
cout << "请输入操作选项:";
while (!(cin >> choice)) {
Utils::clearInputBuffer();
cout << "输入无效!请输入数字:";
}
Utils::clearInputBuffer();
switch (choice) {
case 1: // 添加学生读者
cout << "\n----- 新增学生读者 -----" << endl;
cout << "读者编号:"; getline(cin, readerId);
cout << "读者姓名:"; getline(cin, name);
cout << "学号:"; getline(cin, studentId);
sys.addReader(new StudentReader(readerId, name, 0, studentId));
cout << "学生读者添加成功!" << endl;
Utils::pauseConsole();
break;
case 2: // 添加教师读者
cout << "\n----- 新增教师读者 -----" << endl;
cout << "读者编号:"; getline(cin, readerId);
cout << "读者姓名:"; getline(cin, name);
cout << "教师工号:"; getline(cin, teacherId);
sys.addReader(new TeacherReader(readerId, name, 0, teacherId));
cout << "教师读者添加成功!" << endl;
Utils::pauseConsole();
break;
case 3: // 修改读者信息
cout << "\n----- 修改读者信息 -----" << endl;
cout << "请输入要修改的读者编号:"; getline(cin, readerId);
reader = sys.findReader(readerId);
if (!reader) {
cout << "读者编号不存在!" << endl;
Utils::pauseConsole();
break;
}
cout << "当前读者姓名:" << reader->getName() << ",请输入新姓名(直接回车则不修改):";
getline(cin, name);
if (!name.empty()) reader->setName(name);
cout << "读者信息修改成功!" << endl;
Utils::pauseConsole();
break;
case 4: // 删除读者
cout << "\n----- 删除读者 -----" << endl;
cout << "请输入要删除的读者编号:"; getline(cin, readerId);
sys.deleteReader(readerId);
Utils::pauseConsole();
break;
case 5: // 按编号查找读者
cout << "\n----- 查找读者 -----" << endl;
cout << "请输入读者编号:"; getline(cin, readerId);
reader = sys.findReader(readerId);
if (reader) {
cout << "\n【读者信息】" << endl;
if (StudentReader* stu = dynamic_cast<StudentReader*>(reader)) stu->showInfo();
else if (TeacherReader* tea = dynamic_cast<TeacherReader*>(reader)) tea->showInfo();
}
else {
cout << "未找到该读者!" << endl;
}
Utils::pauseConsole();
break;
case 6: // 查看所有读者
cout << "\n----- 全部读者列表 -----" << endl;
sys.showAllReaders();
Utils::pauseConsole();
break;
case 0: // 返回主菜单
return;
default:
cout << "无效选项!请重新输入。" << endl;
Utils::pauseConsole();
break;
}
}
}
// ===================== 借阅归还子菜单(完整版)=====================
void borrowReturnMenu(LibrarySystem& sys) {
int choice = 0;
string bookId, readerId;
Book* book = nullptr;
Reader* reader = nullptr;
while (true) {
system("cls");
cout << "================ 借阅归还子菜单 ================" << endl;
cout << "1. 图书借阅" << endl;
cout << "2. 图书归还" << endl;
cout << "3. 查看读者借阅记录" << endl;
cout << "4. 查看图书借阅状态" << endl;
cout << "0. 返回主菜单" << endl;
cout << "================================================" << endl;
cout << "请输入操作选项:";
while (!(cin >> choice)) {
Utils::clearInputBuffer();
cout << "输入无效!请输入数字:";
}
Utils::clearInputBuffer();
switch (choice) {
case 1: // 图书借阅
cout << "\n----- 图书借阅 -----" << endl;
cout << "图书编号:"; getline(cin, bookId);
cout << "读者编号:"; getline(cin, readerId);
if (sys.borrowBook(bookId, readerId)) {
cout << "借阅成功!借阅日期:" << Utils::getCurrentDate() << endl;
}
else {
cout << "借阅失败!(图书/读者不存在/已借出/达借阅上限)" << endl;
}
Utils::pauseConsole();
break;
case 2: // 图书归还
cout << "\n----- 图书归还 -----" << endl;
cout << "请输入要归还的图书编号:"; getline(cin, bookId);
if (sys.returnBook(bookId)) {
cout << "归还成功!归还日期:" << Utils::getCurrentDate() << endl;
}
else {
cout << "归还失败!(图书不存在/未借出)" << endl;
}
Utils::pauseConsole();
break;
case 3: // 查看读者借阅记录
cout << "\n----- 读者借阅记录 -----" << endl;
cout << "请输入读者编号:"; getline(cin, readerId);
sys.showBorrowRecordByReader(readerId);
Utils::pauseConsole();
break;
case 4: // 查看图书借阅状态
cout << "\n----- 图书借阅状态 -----" << endl;
cout << "请输入图书编号:"; getline(cin, bookId);
book = sys.findBook(bookId);
if (book) {
cout << "图书《" << book->getBookName() << "》状态:"
<< (book->getIsBorrowed() ? "已借出" : "可借阅") << endl;
}
else {
cout << "图书编号不存在!" << endl;
}
Utils::pauseConsole();
break;
case 0: // 返回主菜单
return;
default:
cout << "无效选项!请重新输入。" << endl;
Utils::pauseConsole();
break;
}
}
}
// ===================== 系统管理子菜单(完整版)=====================
void systemManageMenu(LibrarySystem& sys) {
int choice = 0;
while (true) {
system("cls");
cout << "================ 系统管理子菜单 ================" << endl;
cout << "1. 生成借阅报表" << endl;
cout << "2. 手动保存数据" << endl;
cout << "3. 查看系统数据统计" << endl;
cout << "0. 返回主菜单" << endl;
cout << "================================================" << endl;
cout << "请输入操作选项:";
while (!(cin >> choice)) {
Utils::clearInputBuffer();
cout << "输入无效!请输入数字:";
}
Utils::clearInputBuffer();
switch (choice) {
case 1: // 生成借阅报表
cout << "\n----- 图书馆借阅报表 -----" << endl;
printReport(sys);
Utils::pauseConsole();
break;
case 2: // 手动保存数据
sys.saveData();
cout << "数据已手动保存到本地文件!" << endl;
Utils::pauseConsole();
break;
case 3: // 查看系统数据统计
cout << "\n----- 系统数据统计 -----" << endl;
cout << "总图书数量:" << sys.getBookCount() << endl;
cout << "总读者数量:" << sys.getReaderCount() << endl;
cout << "当前借出图书数:" << sys.getBorrowedBookCount() << endl;
Utils::pauseConsole();
break;
case 0: // 返回主菜单
return;
default:
cout << "无效选项!请重新输入。" << endl;
Utils::pauseConsole();
break;
}
}
}
// ===================== 主菜单(完整版)=====================
int main() {
LibrarySystem sys;
int choice = 0;
cout << "======================================================================" << endl;
cout << "| .----------------------. |" << endl;
cout << "| | | |" << endl;
cout << "| | | |" << endl;
cout << "| .-----------------. | LIBRARY | .-----------------. |"<< endl;
cout << "| | | | | | | |" << endl;
cout << "| | [BOOKS] | | | | [READERS] | |" << endl;
cout << "| | [_____] | | | | [_____] | |" << endl;
cout << "| | [_____] | | | | [_____] | |" << endl;
cout << "| | [_____] | | | | [_____] | |" << endl;
cout << "| | [_____] | | | | [_____] | |" << endl;
cout << "| | | | | | | |" << endl;
cout << "| '-----------------' | | '-----------------' |" << endl;
cout << "| | | |" << endl;
cout << "| | | |" << endl;
cout << "| '----------------------' |" << endl;
cout << "| :: Library Management System :: |" << endl;
cout << "======================================================================" << endl;
Utils::pauseConsole();
while (true) {
system("cls");
cout << "================ 图书管理系统 ================" << endl;
cout << "1. 图书管理" << endl;
cout << "2. 读者管理" << endl;
cout << "3. 借阅归还管理" << endl;
cout << "4. 系统管理" << endl;
cout << "0. 退出系统" << endl;
cout << "=============================================" << endl;
cout << "请输入主菜单操作选项:";
while (!(cin >> choice)) {
Utils::clearInputBuffer();
cout << "输入无效!请输入数字:";
}
Utils::clearInputBuffer();
switch (choice) {
case 1:
bookManageMenu(sys);
break;
case 2:
readerManageMenu(sys);
break;
case 3:
borrowReturnMenu(sys);
break;
case 4:
systemManageMenu(sys);
break;
case 0:
sys.saveData();
cout << "\n系统已自动保存数据,感谢使用!再见~" << endl;
return 0;
default:
cout << "无效选项!请重新输入。" << endl;
Utils::pauseConsole();
break;
}
}
}
代码讲解:
- 分层菜单:主菜单包含四大模块,每个模块对应子菜单,逻辑清晰;
- 输入校验:通过
while(!(cin >> choice))拦截非数字输入,clearInputBuffer解决输入缓冲区异常; - 交互优化:
system("cls")清屏、pauseConsole暂停,提升用户体验; - 自动保存:退出系统时自动调用
saveData,保证数据不丢失。
🎋运行效果展示
💯程序启动界面

- 启动后显示个性化欢迎界面,自动加载本地数据(首次运行提示创建新文件)。
💯图书管理模块

- 支持小说/教材分类添加,自动校验编号唯一性;
- 可修改图书名称/作者,删除前校验"是否已借出"。
💯读者管理模块

- 区分学生/教师读者,自动设置不同借阅上限;
- 删除读者前校验"是否有未归还图书",避免数据异常。
💯借阅归还模块

- 借阅时校验"图书/读者存在性""图书状态""借阅上限";
- 归还时自动匹配借阅记录,更新图书状态和读者借阅数。
💯系统管理模块

- 生成可视化报表,展示总图书数、已借出数、未归还记录数等核心统计;
- 支持手动保存数据、查看系统统计。
🤔可优化方向
本项目为期末大作业基础版本,后续可扩展:
- 登录权限:添加管理员账号密码,区分普通/管理员操作;
- 模糊查询:支持按书名、作者、姓名关键词查找图书/读者;
- 逾期提醒:计算借阅时长(如超过14天),标记逾期未还记录;
- 图形界面:用EasyX图形库替代控制台,提升交互体验;
- 智能指针 :用
unique_ptr替代裸指针,避免内存泄漏。
🎯总结与心得体会
- 通过本次图书管理系统的开发,我深入理解了C++面向对象的核心思想(封装、继承、多态),掌握了STL容器的场景化应用和文件IO的实际操作。
- 开发过程中踩过不少坑:比如输入缓冲区异常导致的交互bug、派生类类型判断错误、文件路径书写错误等,通过调试和查阅资料逐一解决,不仅提升了代码能力,也培养了"容错思维"------好的程序不仅要实现功能,还要能处理各种异常情况。
- 这份系统完全满足C++期末大作业的要求,也让我体会到"工程化代码"的重要性:规范的文件结构、清晰的注释、完善的容错逻辑,能大幅提升代码的可维护性。希望这份代码能帮助到正在做C++期末大作业的同学!