在C++高性能场景下,经常需要把内存中的 std::unordered_set 持久化或通过网络发送。由于该容器底层由桶数组和链表节点组成,直接取地址拷贝完全不可行,必须按元素逐个编码成连续的二进制格式。

为什么不能直接内存dump
std::unordered_set 的每个元素被包装在分配在堆上的节点里,节点含有指向下一个节点的指针与哈希值。即便使用自定义分配器,其内存布局也依赖实现且包含机器字长的指针,跨进程或跨平台读取必然错乱。
基础方案:遍历写入原生字节
当元素类型是平凡可复制(trivially copyable)时,可将其二进制表示直接写入输出流。下面以存储 uint64_t 的集合为例:
#include <unordered_set>
#include <fstream>
#include <cstdint>
void export_set(const std::unordered_set<uint64_t>& s, const char* path) {
std::ofstream out(path, std::ios::binary);
uint64_t size = s.size();
out.write(reinterpret_cast<const char*>(&size), sizeof(size));
for (const auto& v : s) {
out.write(reinterpret_cast<const char*>(&v), sizeof(v));
}
}
读取时先读数量,再循环读回每个值即可。该方式无额外内存分配,速度最快。
改进方案:先搬进vector再整体输出
若希望减少系统调用次数,可先把元素复制到连续的 std::vector,然后一次 write 写完数据区:
#include <unordered_set>
#include <vector>
#include <fstream>
#include <cstdint>
void export_by_vector(const std::unordered_set<uint64_t>& s, const char* path) {
std::vector<uint64_t> buf(s.begin(), s.end());
std::ofstream out(path, std::ios::binary);
uint64_t size = buf.size();
out.write(reinterpret_cast<const char*>(&size), sizeof(size));
out.write(reinterpret_cast<const char*>(buf.data()), size * sizeof(uint64_t));
}
处理字节序与兼容性
如果二进制流要跨机器,需统一字节序。下面是将 uint64_t 转为小端的辅助函数:
#include <cstdint>
void put_le(uint64_t v, char* dst) {
for (int i = 0; i < 8; ++i) {
dst[i] = static_cast<char>((v >> (8 * i)) & 0xFF);
}
}
写入前对每个元素调用 put_le,读取时再做反向组装,可避免大端小端设备解析错误。
非平凡类型的处理
若集合元素是结构体且含 std::string 等成员,不能拷贝整个对象。应分别序列化每个字段,例如先写字符串长度再写内容。核心是避开指针与填充位,只导出真实业务数据。
| 方案 | 额外内存 | 适用场景 |
|---|---|---|
| 遍历写 | 无 | 本地临时落盘 |
| vector中转 | O(n) | 高频网络发送 |
| 字段级编码 | 依实现 | 复杂对象跨平台 |
根据上述对比,绝大多数单纯去重计数导出需求用遍历写已足够;对延迟极敏感的服务可结合 vector 批量写降低调用开销。
std_unordered_set二进制流序列化修改时间:2026-07-31 11:30:22