You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持堆数据的Boost通用二进制序列化与反序列化函数?

Boost序列化通用保存/加载函数修正方案

问题描述

我正在使用Boost库,希望将序列化后的对象保存到二进制文件并从中读取。现有多个自定义对象(如MyCustomData和MyOtherCustomData),想要实现通用的saveData保存函数和loadData读取函数,通过void指针传递数据,但当前saveData无法正确序列化堆上的数据,求修正方案。

原示例代码:

#include <iostream>
#include <fstream>

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/binary_object.hpp>


// some data
struct MyInfo
{
    int x;
    char z[10];
};

class MyCustomData
{
protected:
    MyInfo* _data;
    std::string _str1;

public:
    MyCustomData(const std::string& s1, int x, const char* z)
        : _str1(s1)
    {
        _data = new MyInfo();
        _data->x = x;
        strcpy_s(_data->z, sizeof(_data->z), z);
    }
    
    virtual ~MyCustomData()
    {
        delete _data;
    }

    virtual std::string print()
    {
        std::string res(_str1);
        res += ", data: " + std::to_string(_data->x) + ", ";
        res.append(_data->z);
        return res;
    }
};

class MyOtherCustomData : public MyCustomData
{
    std::string _str2;

public:
    MyOtherCustomData(const std::string& s1, const std::string& s2, int x, const char* z)
        : MyCustomData(s2, x, z)
        , _str2(s1)
    {
    }

    ~MyOtherCustomData()
    {
    }

    std::string print() override
    {
        std::string res(_str2);
        res += ", " + _str1;
        res += ", data: " + std::to_string(_data->x) + ", ";
        res.append(_data->z);
        return res;
    }
};

// save serialized data to file
bool saveData(std::string path, void* data, std::size_t datasize, std::string& err)
{
    bool res = false;
    std::ofstream fout;

    fout.open(path, std::ios::trunc);
    if (!fout.is_open())
    {
        err = "can't open file " + path;
    }
    else
    {
        boost::archive::binary_oarchive archive(fout);
        archive << boost::serialization::make_binary_object(data, datasize);
        res = true;
    }

    fout.close();
    return res;
}

// load serialized data from file
bool loadData(std::string path, void* data, std::size_t datasize, std::string& err)
{
    bool res = false;
    std::ifstream fin;

    fin.open(path, std::ios::in | std::ios::out);
    if (!fin.is_open())
    {
        err = "can't open file " + path;
    }
    else
    {
        // How to make correct read???
        auto obj = boost::serialization::make_binary_object(data, datasize);
        boost::archive::binary_iarchive archive(fin);
        archive >> obj;
        
        res = true;
    }

    fin.close();
    return res;
}

int main()
{
    std::string err;

    // SAVE
    {
        MyCustomData obj1("my info 1", 1, "info1");
        MyOtherCustomData obj2("other", "my info 2", 3, "info2");

        // save to file
        if (!saveData("data1.dat", &obj1, sizeof(MyCustomData), err))
            std::cout << "save error: " << err.c_str() << std::endl;
        else
            std::cout << "saved: " << obj1.print().c_str() << std::endl;

        // save to file
        if (!saveData("data2.dat", &obj2, sizeof(MyOtherCustomData), err))
            std::cout << "save error: " << err.c_str() << std::endl;
        else
            std::cout << "saved: " << obj2.print().c_str() << std::endl;
    }

    // READ
    {
        MyCustomData obj3("temp 1", 10, "temp1");
        MyOtherCustomData obj4("other temp", "temp 2", 20, "temp2");

        // read from file
        if (!loadData("data1.dat", &obj3, sizeof(MyCustomData), err))
            std::cout << "load error: " << err.c_str() << std::endl;
        else
            std::cout << "loaded: " << obj3.print().c_str() << std::endl;

        // read from file
        if (!loadData("data2.dat", &obj4, sizeof(MyOtherCustomData), err))
            std::cout << "load error: " << err.c_str() << std::endl;
        else
            std::cout << "loaded: " << obj4.print().c_str() << std::endl;
    }
}

问题根源

原代码使用boost::serialization::make_binary_object直接拷贝对象内存快照,这种方式仅适用于无堆分配、无复杂成员、无继承关系的简单POD类型。对于包含堆指针(如_data)、std::string等复杂类型,以及存在继承关系的类,该方法无法递归序列化内部数据,也无法处理多态类型信息,导致序列化不完整。


修正步骤

1. 为自定义类型添加Boost序列化支持

Boost Serialization需要每个可序列化的类提供serialize函数,用于定义如何序列化类的成员:

  • 为MyInfo结构体添加序列化函数,直接序列化其成员
  • 为MyCustomData添加序列化函数,处理指针_data(Boost会自动处理堆内存的序列化)和_str1,同时声明支持继承
  • 为MyOtherCustomData添加序列化函数,先序列化基类部分,再处理子类新增的_str2

2. 重写通用的save/load函数为模板函数

放弃void*传递,改用模板函数保留类型信息,让Boost归档可以正确识别对象类型,完成完整序列化。同时修正文件打开模式:必须添加std::ios::binary,避免文本模式下的字节转换问题。


修改后的完整代码

#include <iostream>
#include <fstream>
#include <string>

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/export.hpp>

// some data
struct MyInfo
{
    int x;
    char z[10];

    // 序列化MyInfo
    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & x;
        ar & z;
    }
};

class MyCustomData
{
protected:
    MyInfo* _data;
    std::string _str1;

    // 序列化函数(protected,让子类可以调用)
    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & _data; // Boost自动处理指针指向的堆内存
        ar & _str1;
    }

    // 必须声明默认构造函数,Boost反序列化需要
    MyCustomData() : _data(nullptr) {}

public:
    MyCustomData(const std::string& s1, int x, const char* z)
        : _str1(s1)
    {
        _data = new MyInfo();
        _data->x = x;
        strcpy_s(_data->z, sizeof(_data->z), z);
    }
    
    virtual ~MyCustomData()
    {
        delete _data;
    }

    virtual std::string print()
    {
        std::string res(_str1);
        res += ", data: " + std::to_string(_data->x) + ", ";
        res.append(_data->z);
        return res;
    }

    // 声明支持序列化继承
    friend class boost::serialization::access;
};

// 导出子类,支持多态序列化(如果需要序列化基类指针指向子类对象的场景)
BOOST_CLASS_EXPORT_KEY(MyOtherCustomData)

class MyOtherCustomData : public MyCustomData
{
    std::string _str2;

    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & boost::serialization::base_object<MyCustomData>(*this); // 序列化基类部分
        ar & _str2; // 序列化子类新增成员
    }

    // 默认构造函数
    MyOtherCustomData() : MyCustomData() {}

public:
    MyOtherCustomData(const std::string& s1, const std::string& s2, int x, const char* z)
        : MyCustomData(s2, x, z)
        , _str2(s1)
    {
    }

    ~MyOtherCustomData()
    {
    }

    std::string print() override
    {
        std::string res(_str2);
        res += ", " + _str1;
        res += ", data: " + std::to_string(_data->x) + ", ";
        res.append(_data->z);
        return res;
    }

    friend class boost::serialization::access;
};

// 通用保存函数(模板版本)
template<typename T>
bool saveData(const std::string& path, const T& data, std::string& err)
{
    try
    {
        std::ofstream fout(path, std::ios::trunc | std::ios::binary);
        if (!fout.is_open())
        {
            err = "无法打开文件: " + path;
            return false;
        }

        boost::archive::binary_oarchive archive(fout);
        archive << data;
        return true;
    }
    catch (const std::exception& e)
    {
        err = "保存失败: " + std::string(e.what());
        return false;
    }
}

// 通用加载函数(模板版本)
template<typename T>
bool loadData(const std::string& path, T& data, std::string& err)
{
    try
    {
        std::ifstream fin(path, std::ios::in | std::ios::binary);
        if (!fin.is_open())
        {
            err = "无法打开文件: " + path;
            return false;
        }

        boost::archive::binary_iarchive archive(fin);
        archive >> data;
        return true;
    }
    catch (const std::exception& e)
    {
        err = "加载失败: " + std::string(e.what());
        return false;
    }
}

int main()
{
    std::string err;

    // SAVE
    {
        MyCustomData obj1("my info 1", 1, "info1");
        MyOtherCustomData obj2("other", "my info 2", 3, "info2");

        if (!saveData("data1.dat", obj1, err))
            std::cout << "保存错误: " << err << std::endl;
        else
            std::cout << "已保存: " << obj1.print() << std::endl;

        if (!saveData("data2.dat", obj2, err))
            std::cout << "保存错误: " << err << std::endl;
        else
            std::cout << "已保存: " << obj2.print() << std::endl;
    }

    // READ
    {
        // 注意:反序列化时对象会被重新构造,初始值会被覆盖
        MyCustomData obj3("temp 1", 10, "temp1");
        MyOtherCustomData obj4("other temp", "temp 2", 20, "temp2");

        if (!loadData("data1.dat", obj3, err))
            std::cout << "加载错误: " << err << std::endl;
        else
            std::cout << "已加载: " << obj3.print() << std::endl;

        if (!loadData("data2.dat", obj4, err))
            std::cout << "加载错误: " << err << std::endl;
        else
            std::cout << "已加载: " << obj4.print() << std::endl;
    }
}

关键说明

  • 默认构造函数:Boost反序列化需要类有默认构造函数(可以是private,只要让boost::serialization::access成为友元)
  • 指针序列化:Boost会自动处理MyInfo* _data的序列化,包括分配内存、写入/读取堆上的数据
  • 多态支持:如果需要序列化基类指针指向子类对象的场景,需要使用BOOST_CLASS_EXPORT_KEY或BOOST_CLASS_EXPORT宏导出子类
  • 文件模式:必须添加std::ios::binary,避免文本模式下换行符等字节被转换,导致序列化数据损坏
  • 模板函数:模板保留了对象的类型信息,让Boost可以调用对应的serialize函数,完成完整的序列化

内容的提问来源于stack exchange,提问作者Pasha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:18:10