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()) 疑似条件写反了,正常读完反而会误抛异常,需要实际跑一次加载验证。
代码(加注释版)
注释解释了两种场景:
- 代码有反直觉痕迹,自然写法就在旁边。
- 代码看起来自然,但背后有隐形决策。
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;
}
/*
我要这天再遮不住我眼,我要这地再埋不住我心。
交流群:1062464078
*/

浙公网安备 33010602011771号