悠悠楠杉
C++二进制序列化实战:从原理到文件存储完整指南
在现代C++开发中,对象序列化是将内存中的对象转换为可以存储或传输的格式的过程。二进制序列化因其高效率和小体积而广受欢迎,特别适合游戏开发、金融系统等性能敏感领域。本文将全面介绍三种主流实现方案。
一、直接内存布局写入(基础方案)
最简单直接的序列化方法是将对象的内存布局直接写入文件。这种方法适用于POD(Plain Old Data)类型,即不包含指针、虚函数等复杂特性的简单结构体。
cpp
include
include
struct PlayerData { // POD类型
int health;
float position[3];
char name[32];
};
void writeToFile(const PlayerData& data, const std::string& filename) {
std::ofstream outFile(filename, std::ios::binary);
if (!outFile) {
throw std::runtimeerror("无法打开文件");
}
outFile.write(reinterpretcast
}
PlayerData readFromFile(const std::string& filename) {
std::ifstream inFile(filename, std::ios::binary);
if (!inFile) {
throw std::runtime_error("无法打开文件");
}
PlayerData data;
inFile.read(reinterpret_cast<char*>(&data), sizeof(data));
return data;
}
优点:实现简单,性能极高
缺点:无法处理复杂对象(含指针、STL容器等),存在字节序问题
二、自定义序列化方法(推荐方案)
对于包含动态内存分配、STL容器等复杂对象,我们需要实现自定义的序列化方法。以下是一个可扩展的框架实现:
cpp
include
include
include
class Serializable {
public:
virtual ~Serializable() = default;
virtual void serialize(std::ostream& out) const = 0;
virtual void deserialize(std::istream& in) = 0;
};
class GameSave : public Serializable {
std::string playerName;
std::vector
int currentLevel;
public:
// 序列化实现
void serialize(std::ostream& out) const override {
sizet nameLen = playerName.size();
out.write(reinterpretcast
out.write(playerName.c_str(), nameLen);
size_t scoreCount = scores.size();
out.write(reinterpret_cast<const char*>(&scoreCount), sizeof(scoreCount));
out.write(reinterpret_cast<const char*>(scores.data()),
scoreCount * sizeof(int));
out.write(reinterpret_cast<const char*>(¤tLevel), sizeof(currentLevel));
}
// 反序列化实现
void deserialize(std::istream& in) override {
size_t nameLen;
in.read(reinterpret_cast<char*>(&nameLen), sizeof(nameLen));
playerName.resize(nameLen);
in.read(&playerName[0], nameLen);
size_t scoreCount;
in.read(reinterpret_cast<char*>(&scoreCount), sizeof(scoreCount));
scores.resize(scoreCount);
in.read(reinterpret_cast<char*>(scores.data()), scoreCount * sizeof(int));
in.read(reinterpret_cast<char*>(¤tLevel), sizeof(currentLevel));
}
// 辅助函数
static void saveToFile(const Serializable& obj, const std::string& filename) {
std::ofstream out(filename, std::ios::binary);
obj.serialize(out);
}
static void loadFromFile(Serializable& obj, const std::string& filename) {
std::ifstream in(filename, std::ios::binary);
obj.deserialize(in);
}
};
关键改进点:
1. 使用接口定义统一规范
2. 处理动态大小数据(先写长度再写内容)
3. 支持STL容器等复杂类型
4. 提供便捷的文件操作封装
三、使用Boost序列化库(高级方案)
对于大型项目,Boost提供的序列化库是更健壮的解决方案:
cpp
include <boost/archive/binary_oarchive.hpp>
include <boost/archive/binary_iarchive.hpp>
include <boost/serialization/vector.hpp>
include <boost/serialization/string.hpp>
class BoostSerializable {
friend class boost::serialization::access;
std::string data;
std::vector<int> items;
template<class Archive>
void serialize(Archive& ar, const unsigned int version) {
ar & data;
ar & items;
}
public:
void save(const std::string& filename) const {
std::ofstream ofs(filename, std::ios::binary);
boost::archive::binary_oarchive oa(ofs);
oa << *this;
}
void load(const std::string& filename) {
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive ia(ifs);
ia >> *this;
}
};
Boost方案优势:
- 自动处理复杂类型(包括多态和指针)
- 版本兼容性支持
- 多种格式支持(二进制、XML、文本)
- 完善的异常处理
性能优化与注意事项
- 缓冲区优化:对于频繁的小数据写入,使用缓冲区可显著提升性能
cpp
std::ofstream out("data.bin", std::ios::binary);
char buffer[8192]; // 8KB缓冲区
out.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
- 字节序问题:跨平台应用需处理大小端问题
cpp
template
void writeSwapped(std::ostream& out, T value) {
union {
T val;
char bytes[sizeof(T)];
} u;
u.val = value;
for (size_t i = 0; i < sizeof(T)/2; ++i)
std::swap(u.bytes[i], u.bytes[sizeof(T)-1-i]);
out.write(u.bytes, sizeof(T));
}
- 错误处理:必须验证文件操作是否成功
cpp
std::ofstream out("data.bin", std::ios::binary);
if (!out) {
throw std::runtime_error("文件打开失败");
}
out.exceptions(std::ofstream::failbit | std::ofstream::badbit);
实际应用建议
- 版本控制:在文件头部写入版本号,便于后续兼容
cpp
struct FileHeader {
char magic[4] = {'S','A','V','E'};
uint32_t version = 1;
// 其他元数据...
};
数据校验:添加校验和或哈希值防止数据损坏
安全考虑:敏感数据应加密后再序列化
性能权衡:根据场景选择可读性(如JSON)或性能(二进制)