【C++期末大作业】图书管理系统(面向对象+STL+数据持久化)

目录

🚀前言

大家好!我是 EnigmaCoder

  • 这是本人C++课程的期末大作业------图书管理系统,基于C++面向对象(OOP)思想开发,全程使用STL容器实现数据存储,无需数据库依赖,通过文本文件完成数据持久化,完整覆盖图书/读者的增删改查、借阅归还、报表统计等核心功能。

🐍项目介绍

本图书管理系统基于C++面向对象编程思想开发,是一套轻量化、无数据库依赖的控制台端图书馆运营管理解决方案,适用于高校C++课程实训及小型图书馆基础管理场景。系统核心功能覆盖图书全生命周期管理、读者分级管控、借阅归还流程闭环、数据持久化存储及运营数据统计五大维度,具体能力如下:

  1. 图书分类管理:支持小说、教材两类图书的精细化管理,可完成图书信息的新增、查询、修改、删除操作,精准存储并展示图书编号、名称、作者、专属属性(小说类型/教材适用年级)及借阅状态,删除环节内置"已借出图书禁止删除"的业务校验规则,保障数据逻辑一致性;
  2. 读者分级管控:采用学生、教师双维度读者体系设计,自动适配差异化借阅上限(学生3本/教师5本),支持读者信息全生命周期管理,可快速追溯指定读者的全部借阅记录;
  3. 借阅归还闭环:构建多维度校验机制(图书/读者存在性、图书借阅状态、读者借阅上限),借阅成功自动生成带日期戳的借阅记录,归还操作同步更新图书状态、读者借阅计数及归还日期,确保借阅流程合规可控;
  4. 数据持久化:基于文本文件实现数据本地存储,系统启动自动加载历史数据,退出或手动触发时完成数据备份,避免数据丢失;
  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,保证数据不丢失。

🎋运行效果展示

💯程序启动界面

  • 启动后显示个性化欢迎界面,自动加载本地数据(首次运行提示创建新文件)。

💯图书管理模块

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

💯读者管理模块

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

💯借阅归还模块

  • 借阅时校验"图书/读者存在性""图书状态""借阅上限";
  • 归还时自动匹配借阅记录,更新图书状态和读者借阅数。

💯系统管理模块

  • 生成可视化报表,展示总图书数、已借出数、未归还记录数等核心统计;
  • 支持手动保存数据、查看系统统计。

🤔可优化方向

本项目为期末大作业基础版本,后续可扩展:

  1. 登录权限:添加管理员账号密码,区分普通/管理员操作;
  2. 模糊查询:支持按书名、作者、姓名关键词查找图书/读者;
  3. 逾期提醒:计算借阅时长(如超过14天),标记逾期未还记录;
  4. 图形界面:用EasyX图形库替代控制台,提升交互体验;
  5. 智能指针 :用unique_ptr替代裸指针,避免内存泄漏。

🎯总结与心得体会

  • 通过本次图书管理系统的开发,我深入理解了C++面向对象的核心思想(封装、继承、多态),掌握了STL容器的场景化应用和文件IO的实际操作。
  • 开发过程中踩过不少坑:比如输入缓冲区异常导致的交互bug、派生类类型判断错误、文件路径书写错误等,通过调试和查阅资料逐一解决,不仅提升了代码能力,也培养了"容错思维"------好的程序不仅要实现功能,还要能处理各种异常情况。
  • 这份系统完全满足C++期末大作业的要求,也让我体会到"工程化代码"的重要性:规范的文件结构、清晰的注释、完善的容错逻辑,能大幅提升代码的可维护性。希望这份代码能帮助到正在做C++期末大作业的同学!
相关推荐
耘田14 小时前
 macOS Launch Agent 定时任务实践指南
java·开发语言·macos
折翅嘀皇虫14 小时前
Epoch / QSBR 内存回收解决ABA问题
c++
云雾J视界14 小时前
告别重复编码:Boost.Optional、Variant和Assign如何提升C++工程可维护性?
c++·自动驾驶·分布式系统·variant·工具链·boost.optional·assign
兮动人1 天前
C语言之指针入门
c语言·开发语言·c语言之指针入门
ada7_1 天前
LeetCode(python)78.子集
开发语言·数据结构·python·算法·leetcode·职场和发展
w陆压1 天前
2.区分C++中相似但不同的类型
c++·c++基础知识
十五年专注C++开发1 天前
CMake进阶:vcpkg中OpenSSLConfig.cmake详解
c++·windows·cmake·openssl·跨平台编译
nbsaas-boot1 天前
Go 项目中如何正确升级第三方依赖(Go Modules 实战指南)
开发语言·后端·golang
郑同学的笔记1 天前
【Eigen教程02】深入Eigen矩阵引擎:模板参数、内存布局与基础操作指南
c++·线性代数·矩阵·eigen