boost--序列化庫serialization

序列化能夠把對象轉化成一個字節流存儲或者傳輸,在須要時再回覆成與原始狀態一致的等價對象。C++標準沒有定義這個功能。boost.serialization以庫的形式提供了這個功能,很是強大,能夠序列化C++中各類類型,並且簡單易用。html

boost.serialization庫必須編譯後才能使用。有關boost庫的編譯能夠參考以前的文章windows下編譯和安裝boost庫.ios

serialization庫把存檔和類型的序列化徹底分離開來,任意的數據類型均可以採用任意格式的存檔保存。因此頭文件被分別放在了兩個目錄下:
<boost/archive/>目錄的頭文件處理序列化的存檔表現形式
<boost/serialization>目錄裏的頭文件提供對各類數據類型的序列化能力。windows

boost.serialization庫位於名稱空間boost.archive,在使用時必須根據須要包含特定的存檔頭文件和序列化頭文件。
例如:數組

#include <boost/archive/text_oarchive.hpp> //文本格式輸入存檔
#include <boost/archive/text_iarchive.hpp> //文本格式輸出存檔
#include <boost/serialization/vector.hpp>  //vector的序列化實現頭文件
using namespace boost:archive;//打開名稱空間

serialization庫的三個基本概念:函數

存檔

存檔在serialization庫中表現爲一系列的字節(不必定是ASCII或者二進制),它對應任意的C++對象,能夠持久化保存並在某個時刻恢復成C++對象。測試

根據存檔格式分爲:spa

純文本格式 text_iarchive text_oarchive指針

xml格式 xml_iarchive xml_oarchiverest

二進制格式 binary_iarchive binary_oarchivecode

根據輸入輸出存檔方向:
輸出存檔(saving) : 把C++對象序列化爲某種格式的字節流

輸入存檔(loading) : 把某種格式的字節流反序列化爲等價的C++對象。

可序列化

只有可序列化的C++類型纔可以被序列化爲字節流,保存到存檔中或這從存檔中恢復。

(1) C++基本類型都是可序列化的,如bool、int、double、enum。

(2) 字符串string、wstring是可序列化的

(3) 自定義類型若是有特定形式的成員函數或者自由函數serialize()也是可序列化的。

(4) 可序列化類型的數組也是可序列化的。

(5) 可序列化類型的指針和引用也是可序列化的。
注:serialization庫支持標準庫裏定義的complex bitset valarray pair
對標準容器支持,包括vector、deque、list、set、map,不支持stack,queue,priority_queue.

序列化和反序列化

序列化和反序列化是兩個互逆的過程。
序列化操做符: operator<< operator&
反序列化操做符: operator>> operator&

使用序列化

//序列化
ofstream ofs("serial.txt"); //輸出文件流
string str("boost serializaiton");

{
    boost::archive::text_oarchive oa(ofs);//文本輸出存檔鏈接到文件流
    oa & str;   //序列化到輸出存檔
}

//反序列化  
ifstream ifs("serial.txt");//文件輸入流
string istr;

{
    boost::archive::text_iarchive ia(ifs); //文本輸入存檔鏈接到文件流
    ia & istr;//從輸入文檔反序列化
}

assert(istr == str);

輸出存檔(如:text_oarchive)的構造函數須要使用一個輸出流,建立輸出存檔後,就可使用operator<<operator&向存檔寫入對象。

輸如存檔(如:text_iarchive)構造時要求使用一個輸入流,使用operator<<operator&執行反序列化。

一個簡單的序列化操做模版類:

//BoostArchive.h
#ifndef _BOOST_ARCHIVE_H_
#define _BOOST_ARCHIVE_H_
#include <list>
#include <fstream>
#include <string>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

using std::list;
using std::ifstream;
using std::ofstream;
using std::string;

template <class T>
class BoostArchive
{
public:
    typedef T entity_type;
    typedef boost::archive::text_iarchive InputArchive;
    typedef boost::archive::text_oarchive OutputArchive;

    BoostArchive(const string & archive_file_path)
        : _file_path_name(archive_file_path)
        , _p_ofs(NULL)
        , _p_output_archive(NULL)
        , _entity_nums(0)
    {
        load_arvhive_info();
    }
    ~BoostArchive()
    {
        close_output();
    }
    //存儲一個對象,序列化
    void store(const entity_type & entity);

    //反序列化, 提取全部對象
    bool restore(list<entity_type> & entitys);

    size_t size() const
    {
        return _entity_nums;
    }

private:
    void save_archive_info()    //保存已序列化的對象個數信息
    {
        ofstream ofs;
        ofs.open(get_archive_info_file_path(),std::ios::out | std::ios::trunc);
        if (ofs.is_open())
        {
            ofs << _entity_nums;
        }
        ofs.close();
    }

    void load_arvhive_info()//讀取已序列化的對象個數信息
    {
        ifstream ifs;
        ifs.open(get_archive_info_file_path(),std::ios_base::in);
        if (ifs.is_open() && !ifs.eof())
        {
            int enity_num = 0;
            ifs >> enity_num;
            _entity_nums = enity_num;
        }
        ifs.close();
    }

    string get_archive_info_file_path()
    {
        return "boost_archive_info.meta";
    }

    void close_output()
    {
        if (NULL != _p_output_archive)
        {
            delete _p_output_archive;
            _p_output_archive = NULL;
            save_archive_info();
        }
        if (NULL != _p_ofs)
        {
            delete _p_ofs;
            _p_ofs = NULL;
        }
    }

private:
    size_t _entity_nums;
    string _file_path_name;
    ofstream * _p_ofs;
    OutputArchive * _p_output_archive;
};

template <class T>
bool BoostArchive<T>::restore( list<entity_type> & entitys )
{
    close_output();
    load_arvhive_info();
    ifstream ifs(_file_path_name);
    if (ifs)
    {
        InputArchive ia(ifs);
        for (size_t cnt = 0; cnt < _entity_nums; ++cnt)
        {
            entity_type entity;
            ia & entity;
            entitys.push_back(entity);
        }
        return true;
    }
    return false;
}

template <class T>
void BoostArchive<T>::store( const entity_type & entity )
{
    if (NULL == _p_output_archive)
    {
        _p_ofs = new ofstream(_file_path_name);
        _p_output_archive = new OutputArchive(*_p_ofs);
    }
    (*_p_output_archive) & entity;
    ++_entity_nums;
}

#endif

自定義類型的序列化

自定義類型可使用兩種方式實現可序列化:侵入式和非侵入式

侵入式可序列化

侵入式方式要求類必須實現以下形式的一個成員函數serialize():

template<typename Archive>
void serialize(Archive & ar, const unsigned int version)
{
    ...
}

第一個參數是被用於序列化或反序列化的存檔,第二個參數是一個整型的版本號。在serialize函數內部,應當使用operator&存取類的全部必要的數據成員。
boost::serialization::access是一個輔助類,聲明瞭一系列的靜態成員函數間接調用自定義類的serialize(),存檔經過它來完成對自定義類的序列化。通常在自定義類中聲明boost::serialization::access爲友元來授予訪問權限,serialize()設置爲private。

侵入式序列化示例:

//自定義類型的序列化 
//侵入式
class Student
{
public:
    Student()
        : _id(-1)
        , _name("")
    {}
    Student(const int id, const string & name)
        : _id(id)
        , _name(name)
    {}

    void add_score(double score)
    {
        _scores.push_back(score);
    }

    double get_avg_score() const
    {
        return (_scores.size() == 0) ? 0 : 
            (std::accumulate(_scores.begin(),_scores.end(),0) / _scores.size());
    }

    string get_student_msg() const
    {
        stringstream ss;
        ss << "\nID: " << _id << "  NAME: " << _name << "\n";
        for (auto iter = _scores.begin(); iter != _scores.end(); ++iter)
        {
            ss << *iter << " ";
        }
        ss << "\nAVG_SCORE: " << get_avg_score();
        return ss.str();
    }

private:
    friend boost::serialization::access;        //聲明友元,授予訪問權限
    template<typename Archive>
    void serialize(Archive & ar, const unsigned int version) //序列化函數
    {
        ar & _id;
        ar & _name;
        ar & _scores;
    }
private:
    string _name;
    int _id;
    vector<double> _scores;
    
};

測試代碼(使用到了上面的輔助類BoostArchive):

void TestArchive()
{
    Student s1(1,"cm");
    s1.add_score(100);
    s1.add_score(80);
    s1.add_score(90);   

    Student s2(2,"cj");
    s2.add_score(100);
    s2.add_score(90);
    s2.add_score(90);

    Student s3(3,"zj");
    s3.add_score(100);
    s3.add_score(60);
    s3.add_score(90);

    string archive_file_name("students.dat");
    BoostArchive<Student> archive(archive_file_name);
    archive.store(s1);  //序列化
    archive.store(s2);
    archive.store(s3);

    list<Student> list1;
    archive.restore(list1); //反序列化,恢復數據
    for (auto iter = list1.cbegin(); iter != list1.end(); ++iter)
    {
        cout << iter->get_student_msg();
    }
}

非侵入式可序列化

侵入式可序列化的缺點是要修改類定義,添加一些代碼。若是某個類定義是沒法修改的,就只能使用非侵入的方式,定義一個以下形式的自由函數:

//非侵入式
namespace boost{
namespace serialization{
    template<typename Archive>
    void serialize(Archive & ar, some_class & t, const unsigned int version)
    {
        ...
    }
}
}

自由函數serialize()與成員函數serialize相似,多了一個自定義類型的參數t,在函數體內用它來完成序列化和反序列化。由於非侵入式不能訪問類的私有成員,因此要求要被序列化的成員爲public。
其次爲了方便編譯器查找自由函數serialize,一般應該定義在名稱空間boost::serialization,或boost::archive和自定義類型所在的名稱空間。

非侵入式序列化示例:

struct Person
{
    Person()
        :_id(-1)
        , _name("")
    {
    }
    Person(int id, const string & name)
        :_id(id)
        , _name(name)
    {
    }

    string get_msg() const
    {
        stringstream ss;
        ss << _id << "  " << _name;
        return ss.str();
    }

    int _id;
    string _name;
};

//非侵入式
namespace boost{
namespace serialization{
    template<typename Archive>
    void serialize(Archive & ar, Person & p, const unsigned int version)
    {
        ar & p._id;
        ar & p._name;
    }
}
相關文章
相關標籤/搜索