05 图书管理系统OO版

05 图书管理系统OO版


作者:abnb250/山中鹤鸣
博客:https://www.cnblogs.com/abnb250
交流群:1062464078
点击返回目录



前情提要

这一节做面向对象版的图书管理:
LibaryItem 抽象基类、Book/Magazine 两个派生类、Storage 抽象存储接口、TextStorage 文本实现,
Libary 负责增删、借还、排序、统计与文件存取,main.cpp 提供菜单。
注释版重点两处:
Libary::add 的参数必须是 unique_ptr& 而非 const&,因为 push_back(std::move(item)) 需要非 const 左值转移所有权;
TextStorage::load 末尾的 if(!is.bad()) 疑似条件写反了,正常读完反而会误抛异常,需要实际跑一次加载验证。



代码(加注释版)

注释解释了两种场景:

  1. 代码有反直觉痕迹,自然写法就在旁边。
  2. 代码看起来自然,但背后有隐形决策。

LibaryItem.h

#ifndef LIBARYITEM_H
#define LIBARYITEM_H

#include <iostream>
#include <algorithm>
#include <cmath>
#include <stdexcept>
#include <string>
#include <fstream>
using std::string;

class LibaryItem{
protected:
    string id;
    string name;
    bool status;
public:
    explicit LibaryItem() : id("unknown"), name("unknown"), status(false) {}
    explicit LibaryItem(string i, string n, bool s) : id(i), name(n), status(s) {}
    virtual ~LibaryItem() {}
    virtual void Show() const = 0;
    virtual bool Check() const = 0;
    virtual string kind() const = 0;
    virtual string getname() const = 0;
    virtual string getid() const = 0;
    virtual void setstatus() = 0;
};

class Book : public LibaryItem{
protected:
    string kind_;
public:
    explicit Book() : LibaryItem(), kind_("unknown") {}
    explicit Book(string i, string n, bool s, string k) : LibaryItem(i, n, s), kind_(k) {}
    void Show() const override;
    bool Check() const override;
    string kind() const override;
    string getname() const override;
    string getid() const override;
    void setstatus() override;
};

class Magazine : public LibaryItem{
protected:
    string kind_;
public:
    explicit Magazine() : LibaryItem(), kind_("unknown") {}
    explicit Magazine(string i, string n, bool s, string k) : LibaryItem(i, n, s), kind_(k) {}
    void Show() const override;
    bool Check() const override;
    string kind() const override;
    string getname() const override;
    string getid() const override;
    void setstatus() override;
};

#endif

LibaryItem.cpp

#include "LibaryItem.h"

void Book::Show() const {
    using namespace std;
    cout << "=======================" << '\n';
    cout << "kind  : " << kind_ << '\n';
    cout << "name  : " << name << '\n';
    cout << "id    : " << id << '\n';
    cout << "status: " << static_cast<int>(status) << '\n';
    cout << "=======================" << '\n';
    return;
}

bool Book::Check() const {
    return status;
}

string Book::kind() const {
    return kind_;
}

string Book::getid() const{
    return id;
}

string Book::getname() const{
    return name;
}

void Book::setstatus(){
    status = status ^ 1;
}

void Magazine::Show() const {
    using namespace std;
    cout << "=======================" << '\n';
    cout << "kind  : " << kind_ << '\n';
    cout << "name  : " << name << '\n';
    cout << "id    : " << id << '\n';
    cout << "status: " << status << '\n';
    cout << "=======================" << '\n';
    return;
}

bool Magazine::Check() const {
    return status;
}

string Magazine::kind() const {
    return kind_;
}

string Magazine::getid() const{
    return id;
}

string Magazine::getname() const{
    return name;
}

void Magazine::setstatus(){
    status = status ^ 1;
}

Libary.h

#ifndef LIBARY_H
#define LIBARY_H

#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <stdexcept>
#include <vector>
#include <memory>
#include "LibaryItem.h"

class Storage{
public:
    virtual ~Storage() = default;
    virtual void save(std::vector<std::unique_ptr<LibaryItem>>& items) = 0;
    virtual std::vector<std::unique_ptr<LibaryItem>> load() = 0;
};

class Libary{
private:
    std::vector<std::unique_ptr<LibaryItem>> items;
    Storage& sto;
public:
    explicit Libary(Storage& stor) : sto(stor) {}
    void add(std::unique_ptr<LibaryItem>& item);
    void detele(std::unique_ptr<LibaryItem>& item);
    void find(std::string ids);
    void list();
    void borrow(std::unique_ptr<LibaryItem>& item);
    void returnitem(std::unique_ptr<LibaryItem>& item);
    void sortwithname();
    void showtotal();
    std::vector<std::unique_ptr<LibaryItem>>& libstream();
    void loadfile();

};

#endif

Libary.cpp

#include "Libary.h"
#include "LibaryItem.h"
using std::cout;
using std::cin;

// 不能改成 const 引用,push_back(std::move(item)) 需要非 const 左值来转移所有权
void Libary::add(std::unique_ptr<LibaryItem>& item) {
    for (const auto& x : items) {
        if (x->getid() == item->getid()) {
            throw std::invalid_argument("duplicate id: " + item->getid());
        }
    }
    items.push_back(std::move(item));
}

void Libary::detele(std::unique_ptr<LibaryItem>& item) {
    int index = 0;
    for(auto &x : items){
        if(x->getname() == item->getname() 
        && x->getid() == item->getid()
        && x->kind() == item->kind()){
            items.erase(items.begin() + index);
            return;
        }
        index++;
    }
    throw std::out_of_range("no find item to delete");
    return ;
}

void Libary::find(std::string ids) {
    int index = 0;
    for(auto &x : items){
        if(ids == x->getid()){
            x->Show();
            return;
        }
        index++;
    }
    throw std::out_of_range("no result");
    return ;
}

void Libary::list() {
    for(auto &x : items){
        x->Show();
    }
    return;
}

void Libary::borrow(std::unique_ptr<LibaryItem>& item) {
    for(auto &x : items){
        if(x->getname() == item->getname() 
        && x->getid() == item->getid()
        && x->kind() == item->kind()){
            if(x->Check() == true ){
                cout << "borrowed" << '\n';
                return;
            }
            x->setstatus();
            return;
        }
    }
    throw std::out_of_range("no find item to borrow");
    return ;
}

void Libary::returnitem(std::unique_ptr<LibaryItem>& item) {
    for(auto &x : items){
        if(x->getname() == item->getname() 
        && x->getid() == item->getid()
        && x->kind() == item->kind()){
            if(x->Check() == false ){
                cout << "not borrowed" << '\n';
                return;
            }
            x->setstatus();
            return;
        }
    }
    throw std::out_of_range("no find item to return");
    return ;
}

void Libary::sortwithname() {
    std::sort(items.begin(), items.end(),[](std::unique_ptr<LibaryItem>& a, std::unique_ptr<LibaryItem>& b){
        return a->getname() < b->getname();
    });
    return;
}

void Libary::showtotal() {
    int cnt = 0, b = 0, nob = 0;
    for(auto &x : items){
        cnt++;
        if(x->Check()) b++;
        else nob++;
    }
    cout << "===================" << '\n';
    cout << "total    : " << cnt << '\n';
    cout << "borrowed : " << b << '\n';
    cout << "haved    : " << nob << '\n';
    cout << "===================" << '\n';

    return;
}

std::vector<std::unique_ptr<LibaryItem>>& Libary::libstream() {
    return items;
}

void Libary::loadfile() {
    items = sto.load();
}

TextStorage.h

#ifndef TEXTSTORAGE_H
#define TEXTSTORAGE_H

#include "Libary.h"
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <memory>
#include <stdexcept>
#include <iomanip>

class TextStorage : public Storage{
public:
    ~TextStorage() {}
    void save(std::vector<std::unique_ptr<LibaryItem>>& itmes) override;
    std::vector<std::unique_ptr<LibaryItem>> load() override;
};

#endif

TextStorage.cpp

#include "TextStorage.h"
#include <fstream>
#include <sstream>

void TextStorage::save(std::vector<std::unique_ptr<LibaryItem>>& items) {
    std::string na = "data.txt";
    std::ofstream os(na);
    if(!os.is_open()){
        throw std::runtime_error("file open failed");
    }

    for(auto &x : items){
        os << "=======================" << '\n';
        os << "kind  : " << x->kind() << '\n';
        os << "name  : " << x->getname() << '\n';
        os << "id    : " << x->getid() << '\n';
        os << "status: " << x->Check() << '\n';
        os << "=======================" << '\n';
    }
    
    if(!os){
        throw std::runtime_error("file save failed");
    }

}

std::vector<std::unique_ptr<LibaryItem>> TextStorage::load() {
    using std::string;
    using std::stringstream;
    string na = "data.txt";
    std::ifstream is(na);
    if(!is.is_open()){
        throw std::runtime_error("file open failed");
    }

    std::vector<std::unique_ptr<LibaryItem>> items;
    string line;
    std::size_t linenum;
    int part = 1;
    string nkind, nname, nid, nstatus;

    while(getline(is, line)){
        if(line.empty()) continue;
        if(line == "=======================") continue;
        string res;
        int pos = 0;
        int l = line.size();
        for(int i = 0; i < l; i++){
            if(line[i] == ':'){
                pos = i;
                break;
            }
        }
        pos += 2;
        for(int i = pos; i < l; i++){
            res += line[i];
        }
        if(part == 1){
            nkind = res;
        }
        else if(part == 2){
            nname = res;
        }
        else if(part == 3){
            nid = res;
        }
        else if(part == 4){
            nstatus = res;
            int x = stoi(nstatus);
            bool y = (x == 1 ? true : false);
            part = 0;
            std::unique_ptr<LibaryItem> item;
            if(nkind == "Book"){
                Book tmp(nid, nname, y, nkind);
                item = std::make_unique<Book>(tmp);
            }
            else if(nkind == "Magazine"){
                Magazine tmp(nid, nname, y, nkind);
                item = std::make_unique<Magazine>(tmp);
            }
            else{
                throw std::runtime_error("unknown kind: " + nkind);
            }
            items.push_back(std::move(item));
        }
        part++;
    }
    

    // 疑似反了:正常读完 is.bad() 为 false,!is.bad() 为 true 会误抛异常;需验证读取正常文件是否抛异常
    if(!is.bad()){
        throw std::runtime_error("file load failed");
    }

    return items;
}

main.cpp

#include "Libary.h"
#include "LibaryItem.h"
#include "TextStorage.h"
#include <iostream>
#include <algorithm>
#include <cmath>
#include <memory>
#include <iomanip>
#include <string>
using std::string;
using std::cout;
using std::cin;

TextStorage sto;
Libary lib(sto);

void madd() {
    cout << "cin id, name, kind" << '\n';
    string id, name, kind;
    bool status = false;
    cin >> id >> name >> kind;
    std::unique_ptr<LibaryItem> item;
    if(kind == "Book"){
        Book bk(id, name, status, kind);
        item = std::make_unique<Book>(bk);
    }
    else if(kind == "Magazine"){
        Magazine ma(id, name, status, kind);
        item = std::make_unique<Magazine>(ma);
    }
    else{
        cout << "unknown kind \n";
        return;
    }
    lib.add(item);
}

void mdelete() {
    cout << "cin id, name, kind" << '\n';
    string id, name, kind;
    bool status = false;
    cin >> id >> name >> kind;
    std::unique_ptr<LibaryItem> item;
    if(kind == "Book"){
        Book bk(id, name, status, kind);
        item = std::make_unique<Book>(bk);
    }
    else if(kind == "Magazine"){
        Magazine ma(id, name, status, kind);
        item = std::make_unique<Magazine>(ma);
    }
    else{
        cout << "unknown kind \n";
        return;
    }
    lib.detele(item);
}

void mset() {
    cout << "old message: " << '\n';
    mdelete();
    cout << "new message: " << '\n';
    madd();
}

void mfind() {
    cout << "cin the id" << '\n';
    string id;
    cin >> id;
    lib.find(id);
}

void mborrow() {
    cout << "cin id, name, kind" << '\n';
    string id, name, kind;
    bool status = false;
    cin >> id >> name >> kind;
    std::unique_ptr<LibaryItem> item;
    if(kind == "Book"){
        Book bk(id, name, status, kind);
        item = std::make_unique<Book>(bk);
    }
    else if(kind == "Magazine"){
        Magazine ma(id, name, status, kind);
        item = std::make_unique<Magazine>(ma);
    }
    else{
        cout << "unknown kind \n";
        return;
    }
    lib.borrow(item);
}

void mreturn() {
    cout << "cin id, name, kind" << '\n';
    string id, name, kind;
    bool status = false;
    cin >> id >> name >> kind;
    std::unique_ptr<LibaryItem> item;
    if(kind == "Book"){
        Book bk(id, name, status, kind);
        item = std::make_unique<Book>(bk);
    }
    else if(kind == "Magazine"){
        Magazine ma(id, name, status, kind);
        item = std::make_unique<Magazine>(ma);
    }
    else{
        cout << "unknown kind \n";
        return;
    }
    lib.returnitem(item);
}

void mlist() {
    lib.list();
}

void msave() {
    sto.save(lib.libstream());
}

void mload() {
    lib.loadfile();
}

void showmain(){
    while(1){
        cout << "=======================" << '\n';
        cout << "[1] add a item" << '\n';
        cout << "[2] delete a item " << '\n';
        cout << "[3] set a item" << '\n';
        cout << "[4] find a item " << '\n';
        cout << "[5] borrow a item " << '\n';
        cout << "[6] return a item " << '\n';
        cout << "[7] list all items " << '\n';
        cout << "[8] save the items " << '\n';
        cout << "[9] load the tiems " << '\n';
        cout << "[10] sort by name" << '\n';
        cout << "[11] show total" << '\n';
        cout << "[0] exit " << '\n';
        cout << '\n';
        cout << "please cin a number" << '\n';
        cout << "=======================" << '\n';
        int choice;
        // 需验证:输入非数字时 choice 未初始化,switch 使用未初始化值 UB;建议输入校验或开 UBSan
        cin >> choice;
        switch(choice){
            case 1: madd(); break;
            case 2: mdelete(); break;
            case 3: mset(); break;
            case 4: mfind(); break;
            case 5: mborrow(); break;
            case 6: mreturn(); break;
            case 7: mlist(); break;
            case 8: msave(); break;
            case 9: mload(); break;
            case 10: lib.sortwithname(); break;
            case 11: lib.showtotal(); break;
            case 0:
                cout << "byebye" << '\n';
                return;
            default:
                cout << "use the right number" << '\n';
                break;
        }
    }
    return;
}

int main(){
    using namespace std;
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    try{
        showmain();
    }catch(const exception& e){
        cout << "error: " << e.what() << '\n';
    }
    

    return;
}








posted @ 2026-11-01 17:36  abnb250  阅读(4)  评论(0)    收藏  举报