序列化可以把對象轉化成一個字節流存儲或者傳輸,在需要時再回復成與原始狀態一致的等價對象。C++標准沒有定義這個功能。boost.serialization以庫的形式提供了這個功能,非常強大,可以序列化C++中各種類型,而且簡單易用。
boost.serialization庫必須編譯后才能使用。有關boost庫的編譯可以參考之前的文章windows下編譯和安裝boost庫.
serialization庫把存檔和類型的序列化完全分離開來,任意的數據類型都可以采用任意格式的存檔保存。所以頭文件被分別放在了兩個目錄下:
<boost/archive/>
目錄的頭文件處理序列化的存檔表現形式
<boost/serialization>
目錄里的頭文件提供對各種數據類型的序列化能力。
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++對象。
根據存檔格式分為:
純文本格式 text_iarchive
text_oarchive
xml格式 xml_iarchive
xml_oarchive
二進制格式 binary_iarchive
binary_oarchive
根據輸入輸出存檔方向:
輸出存檔(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;
}
}