因此,我正在用 C++ 构建一个模拟文件系统,以更好地研究该语言,也许还可以进行一些系统级编程。我使用 Boost::Serialization 在用户退出时保存文件系统的状态,但我在保存/加载类时遇到问题。 这是我的基类:
enter code here
enum filetype { FSFILE, FSDIRECTORY, ROOT };
class FileObject {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int) {
ar & BOOST_SERIALIZATION_NVP(name);
ar & BOOST_SERIALIZATION_NVP(date_of_creation);
ar & BOOST_SERIALIZATION_NVP(type);
}
protected:
std::string name;
std::string date_of_creation;
filetype type;
这是我的第一个派生类,它基本上是系统中的一个
.txt
文件:
class File : public FileObject {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject);
ar & BOOST_SERIALIZATION_NVP(content);
ar & BOOST_SERIALIZATION_NVP(size);
}
protected:
std::string content;
int size;
最后是
Directory
类,它将充当保存文件和/或其他目录的目录:
class Directory : public FileObject {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject);
ar & BOOST_SERIALIZATION_NVP(num_of_contents);
ar & BOOST_SERIALIZATION_NVP(size_of_contents);
for (auto it = this->contents.begin(); it != this->contents.end(); it++) {
ar & BOOST_SERIALIZATION_NVP(it->second);
}
}
protected:
int num_of_contents;
int size_of_contents;
public:
std::unordered_map<std::string, FileObject *> contents;
在我的 main.cc 文件中,我有 2 个函数,一个用于保存,一个用于加载
void save_state(const Directory &s, const char * filename){
// make an archive
std::ofstream ofs(filename);
assert(ofs.good());
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(s);
}
void
restore_state(Directory &s, const char * filename)
{
// open the archive
std::ifstream ifs(filename);
assert(ifs.good());
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(s);
}
最后这是其余的
main.cc
文件,它创建一些文件和目录并保存/加载以用于测试目的:
int main() {
char c;
scanf("%c", &c);
std::string filename(boost::archive::tmpdir());
filename += "/demo_save.xml";
if (c == 's') {
File file("test_file1", filetype::FSFILE);
File file2("test_file2", filetype::FSFILE);
File file3("test_file3", filetype::FSFILE);
File file4("test_file4", filetype::FSFILE);
Directory dir("test_dir1", filetype::FSDIRECTORY);
Directory dir2("test_dir2", filetype::FSDIRECTORY);
dir.insertContent(file.getName(), &file);
dir.insertContent(file2.getName(), &file2);
dir2.insertContent(file3.getName(), &file3);
dir2.insertContent(file4.getName(), &file4);
dir.insertContent(dir2.getName(), &dir2);
save_state(dir, filename.c_str());
}
Directory newd;
if (c == 'l') {
restore_state(newd, filename.c_str());
for (auto it = newd.contents.begin(); it != newd.contents.end(); it++) {
if (it->second->getType() == filetype::FSFILE) {
std::cout << it->first << std::endl;
}
else if (it->second->getType() == filetype::FSDIRECTORY) {
for (auto jt = ((Directory *)it->second)->contents.begin(); jt != ((Directory *)it->second)->contents.end(); jt++) {
std::cout << jt->second->getName() << std::endl;
}
}
}
}
return 0;
}
程序编译正常,但我在第二个循环中出现段错误。从读取
.xml
文件来看,dir2
内的文件未正确序列化。
我的类和函数正确吗?这是序列化包含指向其他类的指针的
unordered_map
的正确方法吗?
正如其他人指出的那样,您存在所有权问题。您可以序列化指针,但反序列化会导致内存泄漏。
相反,让指针成为拥有的。我将使用
unique_ptr
来管理它,而不是编写大量代码来正确管理生命周期。
std::unordered_map<std::string, std::unique_ptr<FileObject> > contents;
然后,您必须确保层次结构是虚拟的,至少添加
virtual ~FileObject() = default;
我选择通过提供
FileObject
虚拟方法来使 print
可流式传输。
最后,注册类型,或根据需要将它们标记为抽象:
BOOST_SERIALIZATION_ASSUME_ABSTRACT(FileObject)
BOOST_CLASS_EXPORT(File)
BOOST_CLASS_EXPORT(Directory)
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <filesystem>
#include <iostream>
#include <set>
namespace fs = std::filesystem;
enum filetype { FSFILE, FSDIRECTORY, ROOT };
class FileObject {
public:
std::string getName() const { return name; }
virtual ~FileObject() = default;
virtual void print(std::ostream& os, std::string const& prefix = "") const {
os << "[" << prefix << "/]" << getName() << std::endl;
}
private:
friend class boost::serialization::access;
template <class Ar> void serialize(Ar& ar, unsigned) {
ar& BOOST_NVP(name) & BOOST_NVP(date_of_creation) & BOOST_NVP(type);
}
protected:
FileObject() = default; // only for deserialization
FileObject(std::string name, filetype type) : name(std::move(name)), type(type) {
// TODO date_of_creation
}
std::string name;
std::string date_of_creation;
filetype type = ROOT;
friend std::ostream& operator<<(std::ostream& os, FileObject const& fo) {
fo.print(os);
return os;
}
};
class File : public FileObject {
public:
File(std::string name, size_t size) : FileObject(std::move(name), FSFILE), size(size) {}
private:
File() = default; // only for deserialization
friend class boost::serialization::access;
template <class Ar> void serialize(Ar& ar, unsigned) {
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject) & BOOST_NVP(content) & BOOST_NVP(size);
}
protected:
std::string content;
size_t size = 0;
};
class Directory : public FileObject {
public:
Directory() : FileObject("/", ROOT) {}
Directory(std::string name, filetype type = FSDIRECTORY) : FileObject(name, type) {
assert(FSDIRECTORY == type);
}
bool insertContent(std::unique_ptr<FileObject> object) {
std::string name = object->getName();
return contents.emplace(std::move(name), std::move(object)).second;
}
private:
std::unordered_map<std::string, std::unique_ptr<FileObject> > contents;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar& ar, unsigned) {
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject) & BOOST_NVP(num_of_contents) &
BOOST_NVP(size_of_contents) & BOOST_NVP(contents);
}
protected:
int num_of_contents;
int size_of_contents;
virtual void print(std::ostream& os, std::string const& prefix) const override {
FileObject::print(os, prefix);
for (auto const& [n, obj] : contents)
if (obj)
obj->print(os, prefix + "/" + getName());
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(FileObject)
BOOST_CLASS_EXPORT(File)
BOOST_CLASS_EXPORT(Directory)
#include <fstream>
static inline void save_state(Directory const& s, fs::path const& filename) {
std::ofstream ofs(filename);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_NVP(s);
}
static inline void restore_state(Directory& s, fs::path const& filename) {
std::ifstream ifs(filename);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_NVP(s);
}
int main(int argc, char** argv) {
std::set<std::string_view> const args(argv + 1, argv + argc);
auto filename = fs::temp_directory_path() / "demo_save.xml";
if (args.contains("save")) {
Directory dir("test_dir1", filetype::FSDIRECTORY);
for (auto name : {"test_file1", "test_file2", "test_file3", "test_file4"})
dir.insertContent(std::make_unique<File>(name, filetype::FSFILE));
dir.insertContent(std::make_unique<Directory>("test_dir2"));
save_state(dir, filename);
}
if (args.contains("load")) {
Directory newd;
restore_state(newd, filename);
std::cout << newd;
}
}
打印
[/]test_dir1
[/test_dir1/]test_file1
[/test_dir1/]test_file2
[/test_dir1/]test_file3
[/test_dir1/]test_dir2
[/test_dir1/]test_file4
xml 包含:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<s class_id="0" tracking_level="1" version="0" object_id="_0">
<FileObject class_id="1" tracking_level="1" version="0" object_id="_1">
<name>test_dir1</name>
<date_of_creation></date_of_creation>
<type>1</type>
</FileObject>
<num_of_contents>4199236</num_of_contents>
<size_of_contents>0</size_of_contents>
<contents class_id="2" tracking_level="0" version="0">
<count>5</count>
<bucket_count>13</bucket_count>
<item_version>0</item_version>
<item class_id="3" tracking_level="0" version="0">
<first>test_file4</first>
<second class_id="4" tracking_level="0" version="0">
<tx class_id="5" class_name="File" tracking_level="1" version="0" object_id="_2">
<FileObject object_id="_3">
<name>test_file4</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
<item>
<first>test_dir2</first>
<second>
<tx class_id_reference="0" object_id="_4">
<FileObject object_id="_5">
<name>test_dir2</name>
<date_of_creation></date_of_creation>
<type>1</type>
</FileObject>
<num_of_contents>0</num_of_contents>
<size_of_contents>0</size_of_contents>
<contents>
<count>0</count>
<bucket_count>1</bucket_count>
<item_version>0</item_version>
</contents>
</tx>
</second>
</item>
<item>
<first>test_file3</first>
<second>
<tx class_id_reference="5" object_id="_6">
<FileObject object_id="_7">
<name>test_file3</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
<item>
<first>test_file2</first>
<second>
<tx class_id_reference="5" object_id="_8">
<FileObject object_id="_9">
<name>test_file2</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
<item>
<first>test_file1</first>
<second>
<tx class_id_reference="5" object_id="_10">
<FileObject object_id="_11">
<name>test_file1</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
</contents>
</s>
</boost_serialization>