《C++ 小程序编写系列》(第四部):实战:简易图书管理系统(类与对象篇)

《C++ 小程序编写系列》(第四部):实战:简易图书管理系统(类与对象篇)

在前三部中,我们从基础语法到结构体、vector 容器、文件操作,实现了计算器和学生成绩管理系统(面向过程编程)。本次第四部,我们将跨越到面向对象编程(OOP)的核心 ------类与对象,通过实现简易图书管理系统,掌握类的封装、构造函数、成员函数等关键知识点,完成从 "面向过程" 到 "面向对象" 的思维转变。

一、核心知识点预热(进阶版)

本次是 C++ 面向对象编程的入门关键,核心知识点如下:

  1. 类与对象 :类是自定义的抽象数据类型,封装了成员变量(数据)成员函数(行为);对象是类的实例化。
  2. 封装 :将成员变量私有化(private),仅通过公有(public)成员函数访问 / 修改,避免数据被直接篡改,提升代码安全性。
  3. 构造函数:与类名相同,无返回值,用于初始化对象的成员变量,简化对象创建流程。
  4. const 成员函数 :在成员函数后加const,保证函数不会修改成员变量,提升代码健壮性。
  5. 文件操作进阶:处理图书库存等数值型数据的持久化,兼容借阅 / 归还后的状态更新。

二、系统需求分析(精简核心功能)

我们实现的简易图书管理系统保留核心业务逻辑,兼顾面向对象特性:

  1. 添加图书:输入图书编号、书名、作者、库存数量,创建图书对象并存储。
  2. 查询图书:支持按图书编号(唯一)或书名(多结果)查询信息。
  3. 借阅图书:按编号找到图书,库存足够时减少库存,提示借阅成功。
  4. 归还图书:按编号找到图书,库存增加,提示归还成功。
  5. 显示所有图书:格式化输出所有图书的完整信息。
  6. 数据持久化:启动时加载文件中的图书数据,退出时保存数据。
  7. 退出系统:安全退出并自动保存数据。

三、完整代码实现(精简版)

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
#include <algorithm>

using namespace std;

// 图书类:封装图书的属性和行为(面向对象核心)
class Book {
private:
    // 私有成员变量:仅能通过类内成员函数访问
    string bookId;    // 图书编号(唯一标识)
    string bookName;  // 书名
    string author;    // 作者
    int stock;        // 库存数量

public:
    // 构造函数:初始化图书对象(无参构造+带参构造)
    Book() = default; // 默认构造函数
    Book(string id, string name, string auth, int stk) 
        : bookId(id), bookName(name), author(auth), stock(stk) {}

    // 公有成员函数:提供对私有成员的访问和操作(封装的体现)
    string getBookId() const { return bookId; } // const:不修改成员变量
    string getBookName() const { return bookName; }
    string getAuthor() const { return author; }
    int getStock() const { return stock; }

    // 借阅图书:库存减1(返回是否成功)
    bool borrowBook() {
        if (stock > 0) {
            stock--;
            return true;
        }
        return false;
    }

    // 归还图书:库存加1
    void returnBook() {
        stock++;
    }

    // 设置图书信息(用于文件加载)
    void setInfo(string id, string name, string auth, int stk) {
        bookId = id;
        bookName = name;
        author = auth;
        stock = stk;
    }

    // 显示单本图书信息
    void showInfo() const {
        cout << left << setw(12) << bookId
             << setw(20) << bookName
             << setw(15) << author
             << setw(8) << stock << endl;
    }
};

// 函数声明(核心功能)
void loadBooks(vector<Book>& books); // 从文件加载图书数据
void saveBooks(const vector<Book>& books); // 保存图书数据到文件
void addBook(vector<Book>& books); // 添加图书
void queryBook(const vector<Book>& books); // 查询图书
void borrowBook(vector<Book>& books); // 借阅图书
void returnBook(vector<Book>& books); // 归还图书
void showAllBooks(const vector<Book>& books); // 显示所有图书
void showMenu(); // 显示菜单

int main() {
    vector<Book> books; // 用vector存储图书对象(类的实例集合)
    loadBooks(books); // 启动时加载数据

    int choice;
    while (true) {
        showMenu();
        cout << "请选择功能(1-7):";
        cin >> choice;
        cin.ignore(); // 忽略换行符

        switch (choice) {
            case 1: addBook(books); break;
            case 2: queryBook(books); break;
            case 3: borrowBook(books); break;
            case 4: returnBook(books); break;
            case 5: showAllBooks(books); break;
            case 6: saveBooks(books); cout << "数据已手动保存!" << endl; break;
            case 7: saveBooks(books); cout << "数据已保存,再见!" << endl; return 0;
            default: cout << "输入错误,请重新选择!" << endl;
        }

        cout << "\n按回车键继续...";
        cin.get();
        system("cls"); // Windows清屏,Linux/macOS用"clear"
    }

    return 0;
}

// 显示系统菜单
void showMenu() {
    cout << "========== 图书管理系统 ==========" << endl;
    cout << "1. 添加图书" << endl;
    cout << "2. 查询图书" << endl;
    cout << "3. 借阅图书" << endl;
    cout << "4. 归还图书" << endl;
    cout << "5. 显示所有图书" << endl;
    cout << "6. 手动保存数据" << endl;
    cout << "7. 退出系统" << endl;
    cout << "==================================" << endl;
}

// 从文件加载图书数据(books.txt)
void loadBooks(vector<Book>& books) {
    ifstream inFile("books.txt");
    if (!inFile.is_open()) {
        cout << "首次使用,暂无图书数据文件!" << endl;
        return;
    }

    string id, name, author;
    int stock;
    while (inFile >> id >> name >> author >> stock) {
        Book book;
        book.setInfo(id, name, author, stock);
        books.push_back(book);
    }
    inFile.close();
    cout << "成功加载" << books.size() << "本图书数据!" << endl;
}

// 保存图书数据到文件
void saveBooks(const vector<Book>& books) {
    ofstream outFile("books.txt");
    if (!outFile.is_open()) {
        cout << "文件打开失败,保存失败!" << endl;
        return;
    }

    for (const auto& book : books) {
        outFile << book.getBookId() << " "
                << book.getBookName() << " "
                << book.getAuthor() << " "
                << book.getStock() << endl;
    }
    outFile.close();
}

// 添加图书(检查编号唯一性)
void addBook(vector<Book>& books) {
    string id, name, author;
    int stock;

    cout << "----- 添加图书 -----" << endl;
    // 校验图书编号唯一性
    while (true) {
        cout << "请输入图书编号:";
        getline(cin, id);
        bool exist = false;
        for (const auto& book : books) {
            if (book.getBookId() == id) {
                exist = true;
                cout << "图书编号已存在,请重新输入!" << endl;
                break;
            }
        }
        if (!exist) break;
    }

    // 输入图书信息
    cout << "请输入图书名称:";
    getline(cin, name);
    cout << "请输入作者:";
    getline(cin, author);
    cout << "请输入库存数量:";
    cin >> stock;

    // 创建图书对象并添加到vector
    Book newBook(id, name, author, stock);
    books.push_back(newBook);
    cout << "图书添加成功!" << endl;
}

// 查询图书(按编号/书名)
void queryBook(const vector<Book>& books) {
    if (books.empty()) {
        cout << "暂无图书数据!" << endl;
        return;
    }

    int choice;
    cout << "----- 查询图书 -----" << endl;
    cout << "1. 按编号查询" << endl;
    cout << "2. 按书名查询" << endl;
    cout << "请选择查询方式:";
    cin >> choice;
    cin.ignore();

    if (choice == 1) {
        // 按编号查询(唯一)
        string id;
        cout << "请输入图书编号:";
        getline(cin, id);

        bool found = false;
        for (const auto& book : books) {
            if (book.getBookId() == id) {
                cout << "\n图书信息:" << endl;
                cout << "编号:" << book.getBookId() << "\t书名:" << book.getBookName() << endl;
                cout << "作者:" << book.getAuthor() << "\t库存:" << book.getStock() << endl;
                found = true;
                break;
            }
        }
        if (!found) cout << "未找到该图书!" << endl;
    } else if (choice == 2) {
        // 按书名查询(多结果)
        string name;
        cout << "请输入图书名称:";
        getline(cin, name);

        bool found = false;
        cout << "\n查询结果:" << endl;
        cout << left << setw(12) << "编号" << setw(20) << "书名" << setw(15) << "作者" << setw(8) << "库存" << endl;
        cout << "-----------------------------------------------------------" << endl;
        for (const auto& book : books) {
            if (book.getBookName().find(name) != string::npos) { // 模糊查询
                book.showInfo();
                found = true;
            }
        }
        if (!found) cout << "未找到相关图书!" << endl;
    } else {
        cout << "输入错误!" << endl;
    }
}

// 借阅图书
void borrowBook(vector<Book>& books) {
    if (books.empty()) {
        cout << "暂无图书数据!" << endl;
        return;
    }

    string id;
    cout << "----- 借阅图书 -----" << endl;
    cout << "请输入图书编号:";
    getline(cin, id);

    for (auto& book : books) { // 非const引用,可修改图书库存
        if (book.getBookId() == id) {
            if (book.borrowBook()) {
                cout << "借阅成功!当前库存:" << book.getStock() << endl;
            } else {
                cout << "库存不足,借阅失败!" << endl;
            }
            return;
        }
    }
    cout << "未找到该图书!" << endl;
}

// 归还图书
void returnBook(vector<Book>& books) {
    if (books.empty()) {
        cout << "暂无图书数据!" << endl;
        return;
    }

    string id;
    cout << "----- 归还图书 -----" << endl;
    cout << "请输入图书编号:";
    getline(cin, id);

    for (auto& book : books) {
        if (book.getBookId() == id) {
            book.returnBook();
            cout << "归还成功!当前库存:" << book.getStock() << endl;
            return;
        }
    }
    cout << "未找到该图书!" << endl;
}

// 显示所有图书信息
void showAllBooks(const vector<Book>& books) {
    if (books.empty()) {
        cout << "暂无图书数据!" << endl;
        return;
    }

    cout << "----- 所有图书信息 -----" << endl;
    cout << left << setw(12) << "编号" << setw(20) << "书名" << setw(15) << "作者" << setw(8) << "库存" << endl;
    cout << "-----------------------------------------------------------" << endl;
    for (const auto& book : books) {
        book.showInfo();
    }
}

四、代码解析(重点:面向对象思想)

1. 类的封装(Book 类)

这是本次代码的核心,体现了面向对象的封装性

  • 私有成员变量bookIdbookNameauthorstock被声明为private,外部无法直接访问或修改,避免了数据被恶意篡改(比如直接将库存改为负数)。
  • 公有成员函数 :提供了访问(getXXX)、修改(setInfo)和行为(borrowBookreturnBook)的接口,所有对私有数据的操作都通过这些函数完成,实现了 "数据隐藏"。

2. 构造函数

  • 我们定义了默认构造函数Book() = default;)和带参构造函数 ,用于初始化图书对象:
    • 带参构造函数通过初始化列表直接初始化成员变量,比在函数体内赋值更高效。
    • 默认构造函数用于文件加载时创建空对象,再通过setInfo方法赋值。

3. const 成员函数

  • getBookId() constshowInfo() const,函数后的const表示该函数不会修改类的任何成员变量,编译器会强制检查这一点,避免意外修改数据,提升代码的健壮性。

4. 类的实例化与存储

  • 我们用vector<Book> books存储多个图书对象(类的实例),vector 的动态特性与类的封装结合,既方便管理对象集合,又保证了数据安全。

5. 核心功能的面向对象实现

  • 借阅 / 归还图书 :直接调用图书对象的borrowBookreturnBook成员函数,将图书的行为封装在类内部,符合 "对象的行为由自身负责" 的面向对象思想(比如,图书自己知道如何处理借阅和归还,而不是在外部函数中直接修改库存)。

五、功能扩展思路

在本基础版本上,可进一步扩展功能,深化面向对象的学习:

  1. 添加图书分类 :在 Book 类中增加category成员变量,支持按分类查询图书。
  2. 借阅记录管理 :创建BorrowRecord类,记录借阅人、借阅时间、归还时间,关联 Book 类。
  3. 用户管理 :创建User类(管理员、普通用户),实现不同权限的操作(如管理员可添加图书,普通用户仅可借阅)。
  4. 图书排序 :重载<运算符,实现按书名、库存等条件排序图书。
  5. 异常处理:添加输入验证(如库存不能为负数),抛出并捕获异常,避免程序崩溃。

六、总结

本次我们通过简易图书管理系统,完成了从 "面向过程" 到 "面向对象" 的关键跨越,掌握了类的封装、构造函数、const 成员函数等核心知识点。面向对象编程是 C++ 的核心特性,它的封装、继承、多态三大特性将帮助我们编写更模块化、可维护、可扩展的代码。

与第三部的学生成绩管理系统(结构体 + 面向过程)相比,本次的类封装让代码的逻辑更清晰,数据更安全,更贴近实际项目的开发模式。下一部我们将继续深入,学习继承与多态,实现更复杂的面向对象程序。

相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔4 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大9 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai9 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说9 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json