c++中如何将对象序列化_c++对象持久化存储的常用方案【详解】

直接用 std::ofstream 写 sizeof(obj) 会出错,因为 C++ 对象含指针、虚表或 STL 成员时,二进制拷贝仅保存地址而非数据,反序列化后导致崩溃;POD 结构若含 std::string 等仍不安全。

为什么 std::ofstream 直接写 sizeof(obj) 会出错

因为 C++ 对象通常含指针、虚函数表、引用成员或非 POD 类型子对象,memcpy 级别二进制拷贝会把内存地址原样保存,反序列化时读出来的指针指向无效地址,程序崩溃或数据错乱。即使对象是 POD(如纯 struct),若含 std::stringstd::vector 等标准容器,其内部指针也不会被正确重建。

boost::serialization 实现可移植的类级序列化

这是最成熟、支持 C++ 原生语义的方案,能自动处理继承、多态、指针共享、STL 容器等复杂情况。前提是类需显式声明序列化逻辑,且所有依赖类型也支持该库。

  • 必须为待序列化类添加 serialize 成员函数,或特化 boost::serialization::serialize 模板
  • 使用 BOOST_SERIALIZATION_SPLIT_MEMBER() 可分离 save/l

    oad 逻辑
  • 归档类型选 boost::archive::binary_oarchive(紧凑)或 boost::archive::text_oarchive(可读、跨平台)
  • 编译需链接 -lboost_serialization,头文件为
#include 
#include 
#include 

struct Person {
    std::string name;
    int age;
    template
    void serialize(Archive& ar, const unsigned int version) {
        ar & name & age;
    }
};

// 序列化
std::ofstream ofs("person.txt");
boost::archive::text_oarchive oa(ofs);
Person p{"Alice", 30};
oa << p;

// 反序列化
std::ifstream ifs("person.txt");
boost::archive::text_iarchive ia(ifs);
Person p2;
ia >> p2;

轻量替代:手动实现 to_json() / from_json()(基于 nlohmann/json)

当目标是配置、状态快照或跨语言交互时,JSON 是更安全、易调试的选择。nlohmann/json 支持直接操作 struct,但需配合 NLOHMANN_DEFINE_TYPE_INTRUSIVENLOHMANN_DEFINE_TYPE_NON_INTRUSIVE 宏。

  • 不支持虚函数、裸指针、循环引用,但对普通业务对象足够健壮
  • 生成文本可读,便于人工检查和 patch,也方便 Web API 复用
  • 编译零依赖,头文件即用,但需 C++17 起支持结构化绑定
  • 注意 std::chrono::time_pointstd::optional 等需额外提供转换逻辑
#include 
using json = nlohmann::json;

struct Config {
    std::string host;
    int port;
    std::vector endpoints;
};

NLOHMANN_DEFINE_TYPE_INTRUSIVE(Config, host, port, endpoints)

// 序列化
Config cfg{"localhost", 8080, {"api/v1", "health"}};
json j = cfg;
std::ofstream("config.json") << j.dump(2);

// 反序列化
std::ifstream f("config.json");
json j2;
f >> j2;
Config cfg2 = j2.get();

生产环境慎用:自定义二进制格式 + reinterpret_cast

仅适用于完全可控的 POD 结构体(如网络协议包、GPU 数据块)、固定 ABI、同版本同架构部署。一旦类加字段、改顺序、换编译器,就无法兼容。

  • 必须用 #pragma pack(1)alignas 消除填充字节
  • 整数字段要统一字节序(推荐 htons/ntohl),否则跨平台失效
  • 字符串不能存 char*,得存长度+字符数组,或改用固定长 std::array
  • 没有版本管理能力,升级字段需额外加 magic number 和 size header

真正需要高性能二进制序列化的场景,建议用 Protocol Buffers 或 FlatBuffers —— 它们生成 C++ 代码、带 schema 版本控制、支持向后兼容,比手写更可靠。