json文件,RAII,多态
源代码目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| BookManager/ ├── CMakeLists.txt # 构建配置(C++17、UTF-8、可执行文件、头文件路径) ├── include/ # 头文件 │ ├── Book.h # 图书基类 + FictionBook / TechnicalBook / ReferenceBook │ ├── FileManager.h # FileManager / FileReader / FileWriter │ └── Library.h # 图书馆类(书目管理、保存/加载) ├── src/ # 源文件 │ ├── main.cpp # 程序入口、菜单、用户输入 │ ├── Book.cpp # Book 及各派生类实现(display、toJson、fromJson) │ ├── FileManager.cpp # 文件打开/关闭、读一行、flush │ └── Library.cpp # 添加/删除/查询/借还、saveToFile、loadFromFile、createBookFromJson └── third_party/ └── json/ └── single_include/ └── nlohmann/ # nlohmann/json 头文件(本地,不联网) ├── json.hpp └── json_fwd.hpp
|
Book类
.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| protected: int id_; std::string title_; std::string author_; int year_; bool borrowed_; static int nextId_ public: Book(const std::string& title, const std::string& author, int year); virtual ~Book() = default;
virtual std::string getType() const = 0; virtual void display() const; virtual nlohmann::json toJson() const = 0; virtual void fromJson(const nlohmann::json& j);
|
其余是通用的get方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| class FictionBook : public Book { public: FictionBook(const std::string& title, const std::string& author, int year, const std::string& genre = "未分类"); std::string getType() const override { return "文学"; } void display() const override; nlohmann::json toJson() const override; void fromJson(const nlohmann::json& j) override; const std::string& getGenre() const { return genre_; }
private: std::string genre_; };
|
override关键字标明这个成员函数是重写基类里的虚函数,不是新函数。如果基类里没有同名、同参数、可被重写的虚函数,编译器会报错。
1 2 3 4 5 6 7 8
| class TechnicalBook : public Book{ private: std::string field_; }; class ReferenceBook : public Book{ private: bool encyclopedia_; };
|
.cpp
1 2 3 4 5 6 7 8
| void Book::fromJson(const nlohmann::json& j) { if (j.contains("id")) id_ = j["id"].get<int>(); if (j.contains("title")) title_ = j["title"].get<std::string>(); if (j.contains("author")) author_ = j["author"].get<std::string>(); if (j.contains("year")) year_ = j["year"].get<int>(); if (j.contains("borrowed")) borrowed_ = j["borrowed"].get<bool>(); if (id_ >= nextId_) nextId_ = id_ + 1; }
|
根据一个 nlohmann::json 对象,把里面的 id、title、author、year、borrowed 填到当前 Book 的成员里;如果 JSON 里没有某个键就不改对应成员。
1 2 3 4 5 6 7 8 9 10 11
| nlohmann::json FictionBook::toJson() const { nlohmann::json j = nlohmann::json::object(); j["type"] = "FICTION"; j["id"] = id_; j["title"] = title_; j["author"] = author_; j["year"] = year_; j["borrowed"] = borrowed_; j["genre"] = genre_; return j; }
|
1 2 3 4
| void FictionBook::fromJson(const nlohmann::json& j) { Book::fromJson(j); if (j.contains("genre")) genre_ = j["genre"].get<std::string>(); }
|
其余子类同理
FileManager
.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Book; class FileManager { public: explicit FileManager(const std::string& path); ~FileManager();
FileManager(const FileManager&) = delete; FileManager& operator=(const FileManager&) = delete;
bool isOpen() const { return file_.is_open(); } bool good() const { return file_.good(); } std::fstream& stream() { return file_; } const std::string& path() const { return path_; }
private: std::string path_; std::fstream file_; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class FileReader { public: explicit FileReader(const std::string& path); ~FileReader();
FileReader(const FileReader&) = delete; FileReader& operator=(const FileReader&) = delete;
bool isOpen() const { return file_.is_open(); } std::ifstream& stream() { return file_; } std::string readLine(); bool eof() const { return file_.eof(); }
private: std::string path_; std::ifstream file_; }; class FileWriter { public: explicit FileWriter(const std::string& path); ~FileWriter();
FileWriter(const FileWriter&) = delete; FileWriter& operator=(const FileWriter&) = delete;
bool isOpen() const { return file_.is_open(); } std::ofstream& stream() { return file_; }
private: std::string path_; std::ofstream file_; };
|
删除了拷贝构造与复制=
.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13
| FileManager::FileManager(const std::string& path) : path_(path) { file_.open(path, std::ios::in | std::ios::out | std::ios::app); if (!file_.is_open()) { file_.clear(); file_.open(path, std::ios::out); } }
FileManager::~FileManager() { if (file_.is_open()) { file_.close(); } }
|
RAII:构造时拿资源,析构里还资源,文件的生命周期与FileManager对象绑定。读写器同理。
1 2 3 4 5 6 7 8 9 10 11 12 13
| std::string FileReader::readLine() { std::string line; if (file_.is_open() && std::getline(file_, line)) { return line; } return ""; } FileWriter::~FileWriter() { if (file_.is_open()) { file_.flush(); file_.close(); } }
|
class Library
.h
1 2 3 4 5 6 7 8 9 10 11
| #include <nlohmann/json.hpp> class Library{ private: std::vector<std::unique_ptr<Book>> books_; std::string dataPath_;
std::unique_ptr<Book> createBookFromJson(const nlohmann::json& j); public: bool loadFromFile(const std::string& path); bool saveToFile(const std::string& path) const; }
|
.cpp
1 2 3 4 5 6
| void Library::addBook(std::unique_ptr<Book> book) { if (book) { books_.push_back(std::move(book)); std::cout << "添加成功\n"; } }
|
这里必须使用move,unique_ptr禁用了拷贝,直接push_back会编译报错,需要用move转移所有权。
1 2 3 4 5 6 7 8 9 10
| void Library::removeBook(int id) { auto it = std::find_if(books_.begin(), books_.end(), [id](const std::unique_ptr<Book>& b) { return b->getId() == id; }); if (it != books_.end()) { books_.erase(it); std::cout << "删除成功\n"; } else { std::cout << "未找到该图书\n"; } }
|
lambda表达式。[id]是捕获的变量,从外层作用域拿到的,(const std::unique_ptr& b)是参数,调用时find_if传进来的。