c++如何将对象序列化_c++对象持久化存储方案【实战】

1次阅读

直接用std::ofstream写sizeof对象失败,因c++对象含指针、虚表或std::String等非pod成员,导致反序列化时悬空指针、vptr错乱、内部数据丢失;仅pod类型可临时用write,但跨平台/编译器易失效;必须显式序列化逻辑,推荐boost::serialization或nlohmann::json

c++如何将对象序列化_c++对象持久化存储方案【实战】

为什么 std::ofstream 直接写 sizeof 对象通常失败

因为 C++ 对象常含指针、虚函数表、动态分配内存或非 POD 类型成员,sizeof 只拷贝内存布局快照,反序列化时指针悬空、vptr 错乱、std::stringstd::vector 内部数据丢失。这不是“写不进去”,而是“写进去了也读不回来”。

  • POD 类型(如纯 int/double 成员的 Struct)可临时用 write(reinterpret_cast<const char>(&obj), sizeof(obj))</const>,但跨平台/编译器/版本极易失效
  • std::stringstd::shared_ptr继承关系、对齐差异的类,必须显式定义序列化逻辑
  • 即使自己手写二进制序列化,也要处理字节序(htons/ntohl)、对齐填充、版本兼容性

boost::serialization 快速支持自定义类

它不依赖内存布局,靠重载 serialize 函数控制每个字段读写顺序和方式,天然支持指针、容器、继承、版本管理。

示例:

#include <boost/archive/binary_oarchive.hpp> #include <boost/archive/binary_iarchive.hpp> #include <fstream>  struct Person {     std::string name;     int age = 0;     template<class Archive>     void serialize(Archive& ar, const unsigned int version) {         ar & name & age; // 顺序必须完全一致     } };  // 序列化 std::ofstream ofs("person.dat", std::ios::binary); boost::archive::binary_oarchive oa(ofs); Person p{"Alice", 30}; oa << p;  // 反序列化 std::ifstream ifs("person.dat", std::ios::binary); boost::archive::binary_iarchive ia(ifs); Person p2; ia >> p2;
  • 必须链接 -lboost_serialization
  • 所有嵌套成员类型(如 std::string)需已注册或自带 serialize 实现
  • 若类有私有成员,需将 serialize 声明为 friend,或设为 public
  • 二进制归档不跨语言;如需 JSON/xml,换用 boost::archive::json_oarchive

轻量替代:手动实现 to_json + nlohmann::json

当目标是可读、跨语言、调试友好且对象结构稳定时,JSON 比二进制更实用。nlohmann/json 支持 ADL(argument-dependent lookup),无需侵入类定义。

立即学习C++免费学习笔记(深入)”;

示例:

#include <nlohmann/json.hpp> using json = nlohmann::json;  struct Person {     std::string name;     int age = 0; };  void to_json(json& j, const Person& p) {     j = json{{"name", p.name}, {"age", p.age}}; }  void from_json(const json& j, Person& p) {     j.at("name").get_to(p.name);     j.at("age").get_to(p.age); }  // 使用 Person p{"Bob", 25}; json j = p; // 自动调用 to_json std::ofstream("person.json") << j.dump(2);  json j2 = json::parse(std::ifstream("person.json")); Person p2; j2.get_to(p2); // 自动调用 from_json
  • 字段名拼写错误、缺失字段会抛 json::exception,建议用 j.at("key") 而非 j["key"]
  • 支持嵌套对象、std::vectorstd::map,自动递归处理
  • 性能弱于二进制,但开发效率和维护性高得多
  • 注意:浮点数精度、时间格式、二进制数据(如图片)需额外编码(base64)

生产环境必须考虑的三个隐藏问题

无论选哪种方案,以下三点在上线后才暴露,但设计阶段就要决策:

  • version 字段怎么加?boost::serialization 通过 serialize 第二参数传入;JSON 需显式存 "version": 2 并在 from_json 中分支处理旧格式
  • 敏感字段(如密码、Token)必须从序列化中排除——boost 用条件逻辑跳过;JSON 在 to_json 里不写,或用 json::patch 后处理
  • 文件损坏怎么办?二进制归档无校验;JSON 可加 CRC32 或 SHA-256 摘要字段,读取时验证;更稳妥的是用数据库sqlite)代替裸文件,自带事务与 WAL 日志

真正的难点不在“怎么序列化”,而在“怎么让下次升级代码还能读出三年前存的数据”。版本控制和向后兼容,比语法细节重要十倍。

text=ZqhQzanResources