跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 图书管理系统:面向对象、STL 与数据持久化

一个基于 C++ 面向对象思想开发的图书管理系统。系统采用 STL 容器(vector/map)进行数据存储,无需外部数据库,通过文件实现数据持久化。核心功能包括图书与读者的增删改查、借阅归还流程闭环、以及运营数据统计。设计上运用了封装、继承和多态特性,定义了 Book、Reader 及其派生类,并通过 LibrarySystem 类统一管理业务逻辑。代码包含完整的控制台交互逻辑、输入校验及文件 IO 处理,适合 C++ 课程实训参考。

花里胡哨发布于 2026/3/22更新于 2026/7/27716 浏览
C++ 图书管理系统:面向对象、STL 与数据持久化

项目介绍

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

  1. 图书分类管理:支持小说、教材两类图书的精细化管理,可完成图书信息的新增、查询、修改、删除操作,内置'已借出图书禁止删除'的业务校验规则。
  2. 读者分级管控:采用学生、教师双维度读者体系设计,自动适配差异化借阅上限(学生 3 本/教师 5 本)。
  3. 借阅归还闭环:构建多维度校验机制,借阅成功自动生成带日期戳的借阅记录,归还操作同步更新状态。
  4. 数据持久化:基于文本文件实现数据本地存储,系统启动自动加载历史数据,退出时完成数据备份。
  5. 统计分析:内置报表生成功能,实时统计总图书量、已借出图书量等核心运营数据。

核心技术栈

技术点应用场景
面向对象(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):

#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):

#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):

#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();
    void BorrowCountSub();
    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):

#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):

#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):

#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):

#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):

#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)

#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)

#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 << "\n系统中暂无读者数据!" << 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("data/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("data/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("data/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("data/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("data/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("data/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):

#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++ 期末大作业的要求,也让我体会到'工程化代码'的重要性:规范的文件结构、清晰的注释、完善的容错逻辑,能大幅提升代码的可维护性。

目录

  1. 项目介绍
  2. 核心技术栈
  3. 项目结构
  4. 核心功能模块详解
  5. 类的设计
  6. 图书类:基类 Book + 派生类 NovelBook/TextBook
  7. 读者类:基类 Reader + 派生类 StudentReader/TeacherReader
  8. 借阅记录类 BorrowRecord
  9. 工具类 Utils:控制台交互优化
  10. 系统核心类 LibrarySystem:业务逻辑总控
  11. 主菜单交互逻辑(main.cpp)
  12. 运行效果展示
  13. 程序启动界面
  14. 图书管理模块
  15. 读者管理模块
  16. 借阅归还模块
  17. 系统管理模块
  18. 可优化方向
  19. 总结与心得体会
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • STM32 运行 AI 大模型的四种方案及案例
  • SpringAI 通过 Ollama 本地部署 Deepseek 模型实现对话机器人
  • Vue3 前端配置指南:VSCode settings.json 与 Prettier
  • 夸克网盘精选资源汇总:电子书、软件与学习素材
  • 薛定谔优化算法原理与实现
  • C++ 汉诺塔问题:递归与非递归实战解析
  • C++ libxl 库实现 Excel 文件读写操作指南
  • ClaudeCode 与 Figma-MCP 实现前端 UI 代码 1:1 还原指南
  • 5个封神级Claude Skills开源项目,让AI成为你的专属工具管家
  • OpenClaw 对接飞书:让聊天框变成电脑遥控器
  • Go2 机器人强化学习开发实操:从仿真训练到实物部署
  • 基于昇腾 NPU 的 CodeLlama 模型部署与推理实践
  • 非对称加密算法解析:ECC、RSA 与 ECDH
  • JS 逆向断点调试与前端加密对抗及 SRC 实战案例
  • GLM-4.7-Flash 本地 AI 编码助手部署指南
  • Stable Diffusion WebUI 本地部署指南(CUDA/cuDNN/PyTorch 配置)
  • Chroma + Ollama + Llama 3.1 构建本地私有知识库
  • 高德地图 Web 端开发:API 安装与 Marker 地理编码实战
  • 基于ROS与Ego-Planner的无人机动态避障实现
  • Stable Diffusion WebUI 本地部署与实战指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online