行,但仅适用于POD类型;boost::serialization需手动实现serialize()且不跨平台;nlohmann::json需定义to_json/from_json;跨进程必须用带版本的标准化格式。

用 std::ofstream 直接写二进制结构体,行不行?
行,但只适用于最简单的 POD 类型(比如纯 int、double、固定长度数组),且必须确保对象内存布局稳定。一旦类里有 std::string、std::vector、虚函数、继承或对齐变化,直接 write() 就会写入指针地址或无效内存,反序列化时必然崩溃或数据错乱。
常见错误现象:std::ifstream::read() 读出来全是乱码或程序直接 segfault;调试发现 std::string 的内部指针指向非法地址。
- 检查类型是否为 POD:用
std::is_pod_v<t></t>(C++17 起)或std::is_trivially_copyable_v<t></t>(更准确) - 禁用编译器重排:加
#pragma pack(1)或用[[gnu::packed]](仅当真需要紧凑布局时) - 写前确认
sizeof(T)和字段偏移量没被优化干扰(offsetof(T, field)可验证)
用 boost::serialization 做通用序列化,要注意什么?
它能自动处理 std::string、容器、多态、指针共享等复杂情况,但代价是侵入式——你得为每个要序列化的类专门写 serialize() 成员函数,而且必须链接 Boost 库(静态或动态)。
使用场景:已有较成熟 C++ 项目,不介意引入外部依赖,且对象结构常变、含 STL 容器或继承关系。
立即学习“C++免费学习笔记(深入)”;
- 必须在头文件中包含
<boost></boost>等对应头 - 类需声明
friend class boost::serialization::access;并定义serialize()函数 - 二进制存档不跨平台、不跨 Boost 版本兼容:同一份文件在不同机器或 Boost 1.75/1.82 下可能无法读取
- 性能上比裸 memcpy 慢 3–10 倍,尤其含大量小对象时(因元信息开销和运行时类型检查)
手写文本序列化(JSON / INI)时,nlohmann::json 怎么对接自定义类?
适合调试友好、需人工查看或跨语言交互的场景。nlohmann/json 支持 ADL-based 自定义序列化,无需修改库代码,但必须显式定义 to_json() 和 from_json() 两个非成员函数。
容易踩的坑:字段名拼错、类型转换隐式失败(比如把 int 写成 double 字段再读)、缺失 const 引用导致临时对象绑定失败。
-
to_json()第一个参数必须是json&,第二个是const T& -
from_json()第一个参数是const json&,第二个是T& - 字段名务必与 JSON 中完全一致(区分大小写),建议用字符串字面量避免拼写错误
- 读取时用
j.at("field").get<int>()</int>而非j["field"].get<int>()</int>,前者抛异常提示缺失字段
示例片段:
void to_json(nlohmann::json& j, const Person& p) {
j = nlohmann::json{{"name", p.name}, {"age", p.age}};
}
void from_json(const nlohmann::json& j, Person& p) {
p.name = j.at("name").get<std::string>();
p.age = j.at("age").get<int>();
}
跨进程或长期存储时,为什么不能只靠 reinterpret_cast<char></char>?
因为 C++ 对象的内存表示不是契约性的。哪怕同一个编译器,开启 -O2 后字段重排、添加填充字节、启用 __attribute__((ms_abi)) 都可能改变布局;不同平台的字节序、指针宽度、浮点格式更没法统一。所谓“简单序列化”,一旦脱离单次运行、同编译配置、同架构环境,就不可靠。
真正需要持久化的场景(如配置保存、存档、网络传输),必须选有明确定义格式的方案:协议缓冲区(Protobuf)、FlatBuffers、Cap’n Proto,或者至少用带版本号的自定义二进制头 + 字段校验。
- 哪怕只加一行版本号字段(如
uint32_t version = 1;),也能在读取时快速拒绝不兼容格式 - 所有浮点数建议转成 IEEE 754 标准整数再存(
memcpy(&i, &f, sizeof(f))),避免平台差异 - 字符串统一用 UTF-8 编码 + 显式长度前缀(而非依赖 '\0'),否则遇到嵌入空字符就截断
事情说清了就结束。



















