boost.serialization访问私有成员需显式声明friend class boost::serialization::access,且serialize()须为private/protected;无法修改类时应采用boost_serialization_split_member()配合同命名空间的自由save/load函数,通过公有接口操作私有字段。

私有成员直接序列化会编译失败
Boost.Serialization 默认无法访问 private 成员,哪怕你在类里写了 serialize() 成员函数,只要没给库“开后门”,编译器就会报错:类似 error C2248: 'CCar::m_strName' : cannot access private member。这不是 Boost 的限制,是 C++ 访问控制的刚性规则。
常见错误写法是只加 template<class archive> void serialize(...)</class>,却忘了声明友元关系。Boost 通过 boost::serialization::access 类型来触发访问权限放宽,必须显式声明为 friend 才生效。
- 必须在类内部添加
friend class boost::serialization::access; -
serialize()函数本身必须是private或protected(不能是public,否则破坏封装意图) - 如果类已有其他
friend声明,注意逗号分隔或换行格式,避免语法错误
非侵入式序列化绕过类定义修改
当你不能改原始类(比如第三方库里的 struct 或已封装好的 SDK 类),BOOST_SERIALIZATION_SPLIT_MEMBER() + 独立 save()/load() 函数是唯一合规路径。它把序列化逻辑完全抽离到类外,不碰封装边界一根毫毛。
关键点在于:Boost 会在编译期查找匹配的自由函数,而不会尝试访问私有成员——所有读写都通过你提供的公有接口完成。
- 对私有字段,必须用类的公有 getter/setter,例如
ar & make_nvp("name", t.GetName());仅用于保存,反序列化时需先构造对象再调t.SetName(...) - 必须配合
BOOST_SERIALIZATION_SPLIT_MEMBER()宏,否则库不知道要分两步走 - 自由函数需放在与类同名的命名空间内(通常是全局或类所在 namespace),否则 ADL 查找不到
archive & 操作符和 > 的行为差异
很多人以为 ar & x 和 ar 可互换,其实不是:<code>& 是双向操作符,自动适配 save/load 上下文; 和 <code>>> 是单向的,仅用于输出/输入 archive,强行混用会导致反序列化失败或静默数据丢失。
典型陷阱:用 text_oarchive 时写 oa 没问题,但换成 <code>text_iarchive 后还写 ia ,编译可能过,运行直接崩溃或读出垃圾值。
- 统一用
ar & obj——这是 Boost 推荐且最安全的写法 / <code>>>仅用于原始类型(int,std::string等)的流式写法,不适用于自定义类- 混合使用(比如部分成员用
&,部分用)会导致 archive 内部状态错乱,尤其在二进制 archive 中极易出错
版本控制字段 version 在反序列化时的实际作用
version 参数不是摆设。当类结构变更(比如删了字段、改了类型),旧存档文件加载新代码时,Boost 靠它跳过不存在的字段或做兼容转换。但前提是:你得在 serialize() 里显式判断 version 值,并手动处理分支逻辑。
否则,哪怕只加了一个新 private 成员,旧数据反序列化时也会因字段数不匹配而抛异常 boost::archive::archive_exception: unregistered class 或更隐蔽的内存越界。
- 初始版本从
0开始,每次破坏性变更递增 - 新增字段必须包裹在
if (version >= 1) { ar & new_field; }中 - 删除字段不用删代码,只需不再序列化它,但保留
version判断逻辑供旧数据兼容
SetName("") 是否合法,也不会阻止反序列化出一个 m_balance = -1000000 的银行账户。这些必须靠类自身的不变量约束(比如构造函数、setter 内部校验),序列化层只管字节搬运——这点容易被忽略,直到线上出现脏数据。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











