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

为什么 std::ofstream 直接写 sizeof 对象通常失败
因为 C++ 对象常含指针、虚函数表、动态分配内存或非 POD 类型成员,sizeof 只拷贝内存布局快照,反序列化时指针悬空、vptr 错乱、std::string 或 std::vector 内部数据丢失。这不是“写不进去”,而是“写进去了也读不回来”。
- POD 类型(如纯
int/double成员的 struct)可临时用write(reinterpret_cast<const char>(&obj), sizeof(obj))</const>,但跨平台/编译器/版本极易失效 - 含
std::string、std::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::vector、std::map,自动递归处理 - 性能弱于二进制,但开发效率和维护性高得多
- 注意:浮点数精度、时间格式、二进制数据(如图片)需额外编码(base64)
生产环境必须考虑的三个隐藏问题
无论选哪种方案,以下三点在上线后才暴露,但设计阶段就要决策:
-
version字段怎么加?boost::serialization通过serialize第二参数传入;JSON 需显式存"version": 2并在from_json中分支处理旧格式 - 敏感字段(如密码、token)必须从序列化中排除——
boost用条件逻辑跳过;JSON 在to_json里不写,或用json::patch后处理 - 文件损坏怎么办?二进制归档无校验;JSON 可加 CRC32 或 SHA-256 摘要字段,读取时验证;更稳妥的是用数据库(SQLite)代替裸文件,自带事务与 WAL 日志
真正的难点不在“怎么序列化”,而在“怎么让下次升级代码还能读出三年前存的数据”。版本控制和向后兼容,比语法细节重要十倍。











