在C++项目中,将运行时的对象状态转换为可存储或传输的字节流,并在之后重建对象,是常见的工程需求。面对Boost.Serialization与Cereal这两个主流方案,开发者往往纠结于如何选择。本文从实际代码出发,剖析两者的核心机制与差异。

一、序列化与反序列化的基本概念
序列化指的是把程序中的对象(包括其成员变量、继承关系、指针指向的数据等)转换为线性格式,如二进制、XML或JSON。反序列化则是逆向过程,从线性格式恢复出内存中的对象。C++没有内建的反射机制,因此必须借助宏、模板或手动编码来告知库哪些字段需要被处理。
对于简单结构,手写转换函数也能应付,但当对象包含嵌套容器、多态指针时,人工维护极易出错。Boost和Cereal都通过模板元编程在编译期生成读写逻辑,只是接口风格和依赖体积差别明显。理解它们的设计取向,才能避免后期重构成本。
二、Boost.Serialization的使用方式
Boost.Serialization是Boost套件的一部分,需链接boost_serialization库。它支持文本、二进制、XML多种存档,并能处理shared_ptr、基类指针等复杂情况。下面是一个自定义类的示例:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <string>
class User {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version) {
ar & name;
ar & age;
}
std::string name;
int age;
public:
User() {}
User(std::string n, int a) : name(n), age(a) {}
};
int main() {
User u("Alice", 30);
std::ofstream ofs("user.txt");
boost::archive::text_oarchive oa(ofs);
oa << u;
ofs.close();
User u2;
std::ifstream ifs("user.txt");
boost::archive::text_iarchive ia(ifs);
ia >> u2;
return 0;
}
上述代码通过友元访问权限和serialize模板方法,让存档类直接读写私有成员。Boost使用ar & var这种对称运算符,既用于保存也用于加载,依靠编译期分发。
Boost的优势在于对多态和指针的完整支持。例如基类指针指向派生类对象时,只需注册类型即可正确还原。但缺点是编译错误信息冗长,且引入整个Boost生态会增加构建时间。对老项目或需持久化复杂对象图的系统,它依然可靠。
三、Cereal轻量库的实践
Cereal仅由头文件组成,无需编译独立库,支持C++11及以上。它提供binary、JSON、XML存档,API类似标准流。同样的功能用Cereal实现如下:
#include <cereal/archives/json.hpp>
#include <cereal/types/string.hpp>
#include <fstream>
#include <string>
class User {
public:
std::string name;
int age;
template<class Archive>
void serialize(Archive& ar) {
ar(CEREAL_NVP(name), CEREAL_NVP(age));
}
User() = default;
User(std::string n, int a) : name(n), age(a) {}
};
int main() {
User u("Bob", 25);
std::ofstream ofs("user.json");
cereal::JSONOutputArchive oa(ofs);
oa(u);
ofs.close();
User u2;
std::ifstream ifs("user.json");
cereal::JSONInputArchive ia(ifs);
ia(u2);
return 0;
}
Cereal用ar(value)函数调用风格,并借助CEREAL_NVP宏保留字段名,便于调试JSON。它不要求友元,成员公开即可,降低了侵入性。
在STL容器支持上,Cereal通过独立头文件(如cereal/types/vector.hpp)扩展,按需包含即可。由于无二进制依赖,它常被用于游戏、嵌入式或需要快速迭代的新服务。不过Cereal对裸指针的支持弱于Boost,更推荐配合智能指针使用。
四、两者对比与选型建议
从易用性看,Cereal语法更现代,错误提示清晰;Boost则胜在生态完整和历史沉淀。以下表格列出关键差异:
| 维度 | Boost.Serialization | Cereal |
|---|---|---|
| 依赖形式 | 需链接动态库 | 纯头文件 |
| 指针与多态 | 原生强支持 | 主要支持智能指针 |
| 存档格式 | 文本/二进制/XML | JSON/二进制/XML |
| 编译速度 | 慢 | 快 |
若你的系统已有Boost依赖,且对象图包含大量基类指针、循环引用,Boost能减少自研成本。若是新项目,追求编译效率和可读性,Cereal是更轻的选择。两者均能满足常规C++对象的持久化,关键在团队熟悉度与部署约束。
实际落地时,建议将序列化逻辑集中在独立模块,避免污染业务类。同时书写版本号字段,以便后续字段变更时兼容旧数据。这样无论选哪个库,都能平稳演进。