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

为什么 std::ofstream 直接写 sizeof(obj) 会出错
因为 C++ 对象通常含指针、虚函数表、引用成员或非 POD 类型子对象,memcpy 级别二进制拷贝会把内存地址原样保存,反序列化时读出来的指针指向无效地址,程序崩溃或数据错乱。即使对象是 POD(如纯 struct),若含 std::string、std::vector 等标准容器,其内部指针也不会被正确重建。
用 boost::serialization 实现可移植的类级序列化
这是最成熟、支持 C++ 原生语义的方案,能自动处理继承、多态、指针共享、STL 容器等复杂情况。前提是类需显式声明序列化逻辑,且所有依赖类型也支持该库。
- 必须为待序列化类添加
serialize成员函数,或特化boost::serialization::serialize模板 - 使用
BOOST_SERIALIZATION_SPLIT_MEMBER()可分离 save/load 逻辑 - 归档类型选
boost::archive::binary_oarchive(紧凑)或boost::archive::text_oarchive(可读、跨平台) - 编译需链接
-lboost_serialization,头文件为<boost/archive/text_oarchive.hpp>等
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
struct Person {
std::string name;
int age;
template<class Archive>
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_INTRUSIVE 或 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE 宏。
- 不支持虚函数、裸指针、循环引用,但对普通业务对象足够健壮
- 生成文本可读,便于人工检查和 patch,也方便 Web API 复用
- 编译零依赖,头文件即用,但需 C++17 起支持结构化绑定
- 注意
std::chrono::time_point、std::optional等需额外提供转换逻辑
#include <nlohmann/json.hpp>
using json = nlohmann::json;
struct Config {
std::string host;
int port;
std::vector<std::string> 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<Config>();
生产环境慎用:自定义二进制格式 + reinterpret_cast<char*>
仅适用于完全可控的 POD 结构体(如网络协议包、GPU 数据块)、固定 ABI、同版本同架构部署。一旦类加字段、改顺序、换编译器,就无法兼容。
立即学习“C++免费学习笔记(深入)”;
- 必须用
#pragma pack(1)或alignas消除填充字节 - 整数字段要统一字节序(推荐
htons/ntohl),否则跨平台失效 - 字符串不能存
char*,得存长度+字符数组,或改用固定长std::array<char, N> - 没有版本管理能力,升级字段需额外加 magic number 和 size header
真正需要高性能二进制序列化的场景,建议用 Protocol Buffers 或 FlatBuffers —— 它们生成 C++ 代码、带 schema 版本控制、支持向后兼容,比手写更可靠。











