【问题标题】:Serialization of Unordered Map Using Fstream使用 Fstream 序列化无序映射
【发布时间】:2021-07-12 17:26:41
【问题描述】:

我需要序列化一个无序的地图。下面的方法使用fstream以二进制方式将数据读写到map中。但是,在清除地图并将其重新加载后,它会打印0 而不是5。当修改为与vector 而非unordered_map 一起使用时,这些函数可以正常工作。

#include <iostream>
#include <unordered_map>
#include <fstream>

using namespace std;

void saveData(unordered_map<int, int>* p_map_ptr) {
    fstream file("data.bin", ios::out | ios::binary);

    file.seekp(0);
    file.write((char*) p_map_ptr, sizeof(*p_map_ptr));
    file.close();
}

void loadData(unordered_map<int, int>* p_map_ptr) {
    fstream file("data.bin", ios::in | ios::binary);

    file.seekg(0);
    file.read((char*) p_map_ptr, sizeof(*p_map_ptr));
    file.close();
}

int main() {
    unordered_map<int, int> map;

    map[0] = 5;
    saveData(&map);
    map.clear();
    loadData(&map);

    cout << map[0];
}

【问题讨论】:

  • 您实际上是在序列化地图对象本身的二进制表示。首先,这是未定义的行为;第二,这行不通。你需要序列化地图的内容——它的元素。如果您不想自己手动执行此操作,请使用一些序列化库(IIRC,Boost.Serialization 应该能够序列化标准库容器)。
  • 简单地将某些对象的二进制表示写入文件严格来说没有序列化。序列化是以允许稍后恢复对象的形式写入对象的行为。当对象包含指针(unordered_map 肯定包含)时,您不能存储指针的地址,然后以有意义的方式从该地址恢复对象的状态
  • 如果您想编写自己的数据序列化,请阅读常见问题解答isocpp.org/wiki/faq/serialization,因为它远非小问题。

标签: c++ file serialization file-io io


【解决方案1】:

容器序列化是个小问题,对于unordered_map,它是作为一个哈希表实现的,它的实现有点复杂,你可以从这个picture 看一眼。

在您的代码中,您希望unordered_map 有一个起始地址(char*) p_map_ptr 和一个固定大小sizeof(*p_map_ptr) 并且具有连续的内存布局,这是错误的。

起始地址(char*) p_map_ptrunordered_map的地址,它可能包含一个桶向量的地址,与元素的地址无关,元素存储在列表中,存储在桶中.

大小sizeof(*p_map_ptr)只是unordered_map的大小,不是所有元素的大小,如果我们把它改成p_map_ptr-&gt;sizes() * sizeof(decltype(p_map_ptr)::value_type)会得到所有元素的总大小,但是我们仍然不能使用这个大小复制内存。由于std::unrodered_map的内存布局比较分散。

一个相当大的选择是使用boost serialization

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <fstream>
#include <iostream>
#include <unordered_map>

using namespace std;

void saveData(const unordered_map<int, int>& map,
              const std::string& file = "data.bin") {
  std::ofstream filestream(file);
  boost::archive::binary_oarchive archive(filestream,
                                          boost::archive::no_codecvt);

  archive << map;
}

void loadData(unordered_map<int, int>* p_map_ptr,
              const std::string& file = "data.bin") {
  std::ifstream filestream(file);
  boost::archive::binary_iarchive archive(filestream,
                                          boost::archive::no_codecvt);

  archive >> *p_map_ptr;
}

int main() {
  unordered_map<int, int> map;

  map[0] = 5;
  map[1] = 6;
  saveData(map);
  map.clear();
  loadData(&map);

  cout << map[0];
}

要构建代码,我们需要添加一个链接标志,对于 Linux:-lboost_serialization

相关答案:要使用text archive 进行序列化和反序列化,它比此处的二进制存档更易于阅读,但性能较差。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-30
    • 2018-02-28
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 2015-10-12
    • 1970-01-01
    相关资源
    最近更新 更多