跳转至

Serialization序列化

  • 功能:将 C++ 应用程序中的对象转换为一个字节序列(binary data, text data, XML)
  • 特点:使用fstream文件流会生成存档文件,用于保存数据
  • 注意事项:存档时数据顺序与读档是顺序保持一致,否则数据将错误
  • 简单示例
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <iostream>

void Save()
{
    std::ofstream wf("ser.txt");
    boost::archive::text_oarchive oar(wf);
    int i = 10;
    oar << i;
    wf.close();
}
void Load()
{
    std::ifstream rf("ser.txt");
    boost::archive::text_iarchive iar(rf);
    int i;
    iar >> i;
    std::cout << i << std::endl;
    rf.close();
}
int main()
{
    //存档
    Save();
    //读档
    Load();
}

1 3种存档文件格式xmltxtbinary

  • 头文件
  • xml
    • boost/archive/xml_iarchive.hpp
    • boost/archive/xml_oarchive.hpp
  • txt
    • boost/archive/text_iarchive.hpp
    • boost/archive/text_oarchive.hpp
  • binary
    • boost/archive/binary_iarchive.hpp
    • boost/archive/binary_oarchive.hpp
  • 注意事项:stream流(如fstream)不要在iarchiveoarchive析构前关闭(即不主动调用close)
  • 示例
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <fstream>
#include <iostream>
void Save()
{
    std::ofstream wft("ser.txt");
    std::ofstream wfx("ser.xml");
    std::ofstream wfb("ser.binary");
    boost::archive::text_oarchive oart(wft);
    boost::archive::xml_oarchive oarx(wfx);
    boost::archive::binary_oarchive oarb(wfb);  
    int i = 10;
    oart << i;
    oarx << BOOST_SERIALIZATION_NVP(i);
    oarb << i;
}
void Load()
{
    std::ifstream rft("ser.txt");
    std::ifstream rfx("ser.xml");
    std::ifstream rfb("ser.binary");
    boost::archive::text_iarchive rart(rft);
    boost::archive::xml_iarchive rarx(rfx);
    boost::archive::binary_iarchive rarb(rfb);
    int it=0,ix=0,ib = 0;
    rart >> it;
    rarx >> BOOST_SERIALIZATION_NVP(ix);
    rarb >> ib;
    std::cout << "rart >> it =" << it << std::endl;
    std::cout << "rarx >> ix =" << ix << std::endl;
    std::cout << "rarb >> ib =" << ib << std::endl;
}
int main()
{
    //存档
    Save();
    //读档
    Load();
}
  • 输出结果
rart >> it =10
rarx >> ix =10
rarb >> ib =10

2 基本数据的存档和读档

  • 基本类型如intboolchar等可以直接调用适当的oarchive存档和iarchive读档
  • 示例
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <iostream>

void Save()
{
    std::ofstream wf("ser.txt");
    boost::archive::text_oarchive oar(wf);
    int i = 10;
    char c = '1';
    bool b = false;
    oar << i;
    oar << c;
    oar << b;
    wf.close();
}
void Load()
{
    std::ifstream rf("ser.txt");
    boost::archive::text_iarchive iar(rf);
    char c;
    int i;
    bool b;
    //读档顺序与存档时保持一致
    iar >> i;
    iar >> c;
    iar >> b;
    std::cout <<"i=" << i << std::endl;
    std::cout <<"c="<< c << std::endl;
    std::cout <<std::boolalpha<< "b=" << b << std::endl;
    rf.close();
}
int main()
{
    //存档
    Save();
    //读档
    Load();
}
  • 输出结果
i=10
c=1
b=false

3 C++容器(list、string、array ...)的存档和读档

STL容器与基本类型唯一不同是需要添加对应的头文件,如vector添加#include<boost/serialization/vector.hpp> - 头文件 - string添加头文件boost/serialization/string.hpp - vector添加头文件boost/serialization/vector.hpp - list添加头文件boost/serialization/list.hpp - array添加头文件boost/serialization/array.hpp - map添加头文件boost/serialization/map.hpp - stack添加头文件boost/serialization/stack.hpp - 其它... - 示例

#include<vector>
#include<iostream>
#include<fstream>
#include<boost/serialization/vector.hpp>
#include<boost/archive/text_iarchive.hpp>
#include<boost/archive/text_oarchive.hpp>
void Save()
{
    std::ofstream wf("ser.txt");
    boost::archive::text_oarchive oar(wf);
    std::vector<std::string> v = { "Hello","World","!" };
    oar << v;
}
void Load()
{
    std::ifstream rf("ser.txt");
    boost::archive::text_iarchive iar(rf);
    std::vector<std::string> v;
    iar >> v;
    for (const std::string& str:v)
    {
        std::cout << str << " ";
    }
    std::cout << std::endl;
}
int main()
{
    //存档
    Save();
    //读档
    Load();
}
  • 输出结果
Hello World !

4 自定义类的存档和读档

  • 声明友元类friend class boost::serialization::access;
  • 类中实现template <typename Archive> void serialize(Archive &ar, const unsigned int version)模板函数

4.1 无继承类

  • 示例
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include<boost/serialization/map.hpp>
#include <fstream>
#include <iostream>
#include<map>
#include<string>
using namespace std;
class Base
{
public:
    Base() :m_i(0), m_pb(NULL), m_mp({}) {}
    Base(int i, Base* pb, std::map<string, int> mp) :m_i(i),m_pb(pb),m_mp(mp){}
private:
    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar& m_i;
        ar& m_pb;
        ar& m_mp;
    }
    int m_i;
    Base* m_pb;
    std::map<string,int> m_mp;
};

void Save()
{
    std::ofstream wf("ser.txt");
    boost::archive::text_oarchive oar(wf);
    Base b(2, nullptr, { {"1",1},{"2",2} });
    oar << b;
}
void Load()
{
    std::ifstream rf("ser.txt");
    boost::archive::text_iarchive iar(rf);
    Base b;
    //读档顺序与存档时保持一致
    iar >> b;
}
int main()
{
    //存档
    Save();
    //读档
    Load();
}

4.2 继承类

  • 在无继承类基础上增加以下几点
  • 添加头文件#include<boost/serialization/base_object.hpp>
  • 在serialize函数中增加序列化基类代码,例如ar& boost::serialization::base_object<Base>(*this);
  • 示例
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include<boost/serialization/base_object.hpp>
#include<boost/serialization/map.hpp>
#include <fstream>
#include <iostream>
#include<map>
#include<string>
using namespace std;
class Base
{
public:
    Base() :m_i(0), m_pb(NULL), m_mp({}) {}
    Base(int i, Base* pb, std::map<string, int> mp) :m_i(i),m_pb(pb),m_mp(mp){}
private:
    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar& m_i;
        ar& m_pb;
        ar& m_mp;
    }
    int m_i;
    Base* m_pb;
    std::map<string,int> m_mp;
};

class Derived :public Base
{
public:
    Derived() = default;
    Derived(string str) :Base(),m_str(str) {}
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar& boost::serialization::base_object<Base>(*this);
        ar& m_str;
    }
    string m_str;

};

void Save()
{
    std::ofstream wf("ser.txt");
    boost::archive::text_oarchive oar(wf);
    Base b(2, nullptr, { {"1",1},{"2",2} });
    Derived d("hello");
    oar << b;
    oar << d;
}
void Load()
{
    std::ifstream rf("ser.txt");
    boost::archive::text_iarchive iar(rf);
    Base b;
    Derived d;
    //读档顺序与存档时保持一致
    iar >> b;
    iar >> d;
}
int main()
{
    //存档
    Save();
    //读档
    Load();
}

5 指针、引用的存档和读档

  • 指针与对象使用一样,但引用需要转换成相应的指针
  • 示例
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include<boost/serialization/map.hpp>
#include <fstream>

using namespace std;
class object;
class my_class {
private:
    friend class boost::serialization::access;
    int member1;
    object& member2;
    template<class Archive>
    friend void serialize(Archive& ar, const unsigned int file_version);
public:
    my_class(int m, object& o) :
        member1(m),
        member2(o)
    {}
};
template<class Archive>
inline void save_construct_data(
    Archive& ar, const my_class* t, const unsigned int file_version
) {
    // save data required to construct instance
    ar << t.member1;
    // serialize reference to object as a pointer
    ar << &t.member2;
}

template<class Archive>
inline void load_construct_data(
    Archive& ar, my_class* t, const unsigned int file_version
) {
    // retrieve data from archive required to construct new instance
    int m;
    ar >> m;
    // create and load data through pointer to object
    // tracking handles issues of duplicates.
    object* optr;
    ar >> optr;
    // invoke inplace constructor to initialize instance of my_class
    ::new(t)my_class(m, *optr);
}

6 扩展

6.1 类序列化也可以save和load分离

  1. 添加头文件boost/serialization/split_member.hpp
  2. 在类中使用BOOST_SERIALIZATION_SPLIT_MEMBER()函数宏
  3. 示例
#include <boost/serialization/string.hpp>
#include<boost/serialization/access.hpp>
#include <boost/serialization/split_member.hpp>
class Base
{
private:
    friend class boost::serialization::access;
    std::string m_str;
    template<class Archive>
    void save(Archive& ar, const unsigned int version)const
    {
        ar << m_str;
    }
    template<class Archive>
    void load(Archive& ar, const unsigned int version)
    {
        ar >> m_str;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
public:
    Base() :m_str("") {}
    Base(std::string str) :m_str(str) {}
};

6.2 serialize函数与类分离,可作为类外函数(可声明类友元函数方便访问类使用成员)

  1. 类成员函数void serialize(Archive& ar, const unsigned int version)改成类外函数,如void serialize(Archive& ar, Base& base, const unsigned int version)
  2. 示例
#include<boost/serialization/map.hpp>
#include<map>
#include<string>
using namespace std;
class Base
{
public:
    Base() :m_i(0), m_pb(NULL), m_mp({}) {}
    Base(int i, Base* pb, std::map<string, int> mp) :m_i(i), m_pb(pb), m_mp(mp) {}
private:
    friend class boost::serialization::access;

    template<class Archive>
    friend void serialize(Archive& ar, Base& base, const unsigned int version);
    int m_i;
    Base* m_pb;
    std::map<string, int> m_mp;
};
template<class Archive>
void serialize(Archive& ar, Base& base,const unsigned int version)
{
    ar& base.m_i;
    ar& base.m_pb;
    ar& base.m_mp;
}

6.3 添加版本信息

  1. 添加头文件boost/serialization/version.hpp
  2. 类外使用BOOST_CLASS_VERSION函数宏
#include <boost/serialization/string.hpp>
#include<boost/serialization/access.hpp>
#include <boost/serialization/version.hpp>
class Base
{
private:
    friend class boost::serialization::access;
    std::string m_str;
    template<class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        if (version > 0)
            ar& m_str;
    }
public:
    Base():m_str("") {}
    Base(std::string str) :m_str(str) {}
};
BOOST_CLASS_VERSION(Base, 1)

7 参考资料