boost序列化(Serialization)

本文章轉載自 http://m.blog.csdn.net/zj510/article/details/8105408


程序開發中,序列化是經常需要用到的。像一些相對高級語言,比如JAVA, C#都已經很好的支持了序列化,那么C++呢?當然一個比較好的選擇就是用Boost,這個號稱C++準標準庫的東西。

什么時候需要序列化呢?舉個例子,我們定義了一個class,比如:

class CCar
{
public:void SetName(std::string& strName){m_strName = strName;}std::string GetName() const{return m_strName;}
private:std::string m_strName;
};


然后我們想把這個類的一個對象保存到文件中或者通過網絡發出去,怎么辦呢?答案就是:把這個對象序列化,然后我們可以得到一個二進制字節流,或者XML格式表示等等。

這樣我們就可以保存這個對象到文件中或者通過網絡發出去了。把序列化的數據進行反序列化,就可以得到一個CCar對象了。

Boost已經很好的支持了序列化這個東西,很好很強大。

Boost網站上有介紹:?http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/index.html

對于序列化,Boost是這么定義的:

Here, we use the term?"serialization"?to mean the reversible deconstruction of an arbitrary set of C++ data structures to a sequence of bytes.? Such a system can be used to reconstitute an equivalent structure in another program context.? Depending on the context, this might used implement object persistence, remote parameter passing or other facility. In this system we use the term"archive"?to refer to a specific rendering of this stream of bytes.? This could be a file of binary data, text data,? XML, or some other created by the user of this library.

這段英文很簡單,我相信大多數程序員都能看的懂。

基本上Boost序列化可以分為兩種模式:侵入式(intrusive)和非侵入式(non-intrusive)

?

侵入式(intrusive)

先來看看侵入式。我們先來定義一個類,這個類支持序列化:

class CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & _tag;ar & _text;}public:CMyData():_tag(0), _text(""){}CMyData(int tag, std::string text):_tag(tag), _text(text){}int GetTag() const {return _tag;}std::string GetText() const {return _text;}private:int _tag;std::string _text;
};


?

其中,我們可以看到這些代碼:

friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & _tag;ar & _text;}

這些代碼就是用來實現序列化的,這些代碼存在于類CMyData中,也就是為什么稱這種模式是“侵入式”的原因了。

看看怎么把這個對象序列化。這里,我把這個對象以二進制的方式保存到了一個ostringstream中了,當然也可以保存為其他形式,比如XML。也可以保存到文件中。代碼都是類似的。

void TestArchive1()
{CMyData d1(2012, "China, good luck");std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData tag: " << d2.GetTag() << ", text: " << d2.GetText() << "\n";
}

先生成一個CMyData的對象,然后序列化保存到一個ostringstream中,接著再把這個序列化的數據反序列化,得到原來的對象,打印出來,我們會發現反序列化的對象的數據成員跟序列化前的對象一模一樣。哈哈,成功了,簡單吧。至于Boost怎么實現這個過程的,看Boost源代碼吧,Boost的網站上也有一些介紹。Boost確實設計的很巧妙,不得不佩服那幫家伙。

那么可以序列化CMyData的子類嗎,答案是肯定的。其實很簡單就是在子類的序列化函數里面先序列化基類的。看看代碼就明白了:

class CMyData_Child: public CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){// serialize base class informationar & boost::serialization::base_object<CMyData>(*this);ar & _number;}public:CMyData_Child():_number(0.0){}CMyData_Child(int tag, std::string text, float number):CMyData(tag, text), _number(number){}float GetNumber() const{return _number;}private:float _number;
};void TestArchive3()
{CMyData_Child d1(2012, "China, good luck", 1.2);std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData_Child d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData_Child tag: " << d2.GetTag() << ", text: " << d2.GetText() << ", number: "<<d2.GetNumber() << "\n";
}


?

?

非侵入式(non-intrusive)

侵入式的缺點就是需要在class里面加一些代碼,那么有時候可能這個class已經存在了,或者我們并不想往里面加入這么些代碼,那么怎么辦呢?ok,輪到非侵入式出場了。

比方說我們有這么個類:

class CMyData2
{
public:CMyData2():_tag(0), _text(""){}CMyData2(int tag, std::string text):_tag(tag), _text(text){}int _tag;std::string _text;
};

那么我們可以這么序列化:

namespace boost {namespace serialization {template<class Archive>void serialize(Archive & ar, CMyData2 & d, const unsigned int version){ar & d._tag;ar & d._text;}} // namespace serialization
} // namespace boost


?然后調用還是跟侵入式一模一樣,看:

void TestArchive2()
{CMyData2 d1(2012, "China, good luck");std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData2 d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData2 tag: " << d2._tag << ", text: " << d2._text << "\n";
}


成功。跟侵入式相比,非侵入式省去了在具體類里面加入序列化代碼。但是我們看看非侵入式模式里面的類的定義,我們會發現我們把數據成員搞成public的了。這是為什么呢?看看這個就明白了:

template<class Archive>void serialize(Archive & ar, CMyData2 & d, const unsigned int version){ar & d._tag;ar & d._text;}

原來序列化函數需要訪問數據成員。這就是非侵入式的一個缺點了:需要把數據成員暴露出來。通過直接訪問數據成員也好,通過函數訪問也好,總之需要這個類把數據成員暴露出來,這樣序列化函數才能訪問。世界上沒有十全十美的東西,有時我們得到一個東西,往往會失去另外一個東西,不是嗎?

侵入式和非侵入式各有各的用處,看具體情況來決定用哪個了。

非侵入式可以支持子類序列化嗎?可以。跟侵入式一樣,其實也就是先序列化一下基類,然后再序列化子類的數據成員。看代碼:


class CMyData2_Child: public CMyData2
{
public:CMyData2_Child():_number(0.0){}CMyData2_Child(int tag, std::string text, float number):CMyData2(tag, text), _number(number){}float _number;
};namespace boost {namespace serialization {template<class Archive>void serialize(Archive & ar, CMyData2_Child & d, const unsigned int version){// serialize base class informationar & boost::serialization::base_object<CMyData2>(d);ar & d._number;}} // namespace serialization
} // namespace boostvoid TestArchive4()
{CMyData2_Child d1(2012, "test non-intrusive child class", 5.6);std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData2_Child d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData2_Child tag: " << d2._tag << ", text: " << d2._text << ", number: "<<d2._number<<"\n";
}

?

好了,以上就是序列化的簡單用法。接下里我們來重點關注一下數據成員的序列化,假如我們的類里面有指針,那么還能序列化嗎?比如下面的代碼,會發生什么事?


序列化指針數據成員

class CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & _tag;ar & _text;}public:CMyData():_tag(0), _text(""){}CMyData(int tag, std::string text):_tag(tag), _text(text){}int GetTag() const {return _tag;}std::string GetText() const {return _text;}private:int _tag;std::string _text;
};
class CMyData_Child: public CMyData
{
private:friend class boost::serialization::access;templatevoid serialize(Archive& ar, const unsigned int version){// serialize base class informationar & boost::serialization::base_object(*this);ar & _number;}public:CMyData_Child():_number(0.0){}CMyData_Child(int tag, std::string text, float number):CMyData(tag, text), _number(number){}float GetNumber() const{return _number;}private:float _number;
};
class CMyData_Container
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){for(int i = 0; i < 3; i++){ar & pointers[i];}}
public:CMyData* pointers[3];
};void TestPointerArchive()
{std::string content;{CMyData d1(1, "a");CMyData_Child d2(2, "b", 1.5);CMyData_Container containter;containter.pointers[0] = &d1;containter.pointers[1] = &d2;containter.pointers[2] = &d1;std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << containter;content = os.str();}//反序列化{CMyData_Container container;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> container;for (int i = 0; i < 3; i++){CMyData* d = container.pointers[i];std::cout << "pointer" << i + 1 <<": " << d->GetText() << "\n";if (i == 1){CMyData_Child* child = reinterpret_cast<CMyData_Child*>(d);std::cout << "pointer" << i + 1 <<", number: " << child->GetNumber() << "\n";}}}
}


?注意,我們在CMyData_Container對象里面放進去了3個指針,其中第二個指針是CMyData的子類。

然后進行序列化,再反序列化,我們會發現,第一個,第三個指針輸出了正確的信息,然而第二個指針有點問題,本身我們存進去的時候是個CMyData_Child 對象,通過測試我們可以發現,CMyData_Child的基類部分,我們可以正確的輸出,但是CMyData_Child的成員_number,卻得不到正確信息。這是個問題。

也就是說,序列化指針是可以的,但是需要注意多態的問題。假如我們不需要考慮多態,那么以上的代碼就可以正常工作了。但是如果要考慮多態的問題,那么就得特殊處理了。下面再來介紹序列化多態指針。

?

?序列化多態指針數據成員

上一個章節里面演示了如果序列化指針成員,但是有個問題,就是當基類指針指向一個派生類對象的時候,然后序列化這個指針,那么派生類的信息就被丟掉了。這個很不好。那么怎么來解決這個問題呢?很幸運,Boost的開發人員已經考慮到了這個問題。再一次感受到Boost的強大。

有兩種方法可以解決這個問題:

1. registration

2. export

具體可以參考:?http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/serialization.html#derivedpointers

這里我們介紹第二種方式,這種方式比較簡單,也用的比較好。就是通過一個宏把派生類給命名一下。

這個關鍵的宏是:BOOST_CLASS_EXPORT_GUID

相關解釋:

The macro?BOOST_CLASS_EXPORT_GUID?associates a string literal with a class. In the above example we've used a string rendering of the class name. If a object of such an "exported" class is serialized through a pointer and is otherwise unregistered, the "export" string is? included in the archive. When the archive? is later read, the string literal is used to find the class which? should be created by the serialization library. This permits each class to be in a separate header file along with its? string identifier. There is no need to maintain a separate "pre-registration"? of derived classes that might be serialized.? This method of registration is referred to as "key export".

如何使用這個神奇的宏BOOST_CLASS_EXPORT_GUID來實現序列化指向派生類的指針呢?先給出代碼:

class CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & _tag;ar & _text;}public:CMyData():_tag(0), _text(""){}CMyData(int tag, std::string text):_tag(tag), _text(text){}virtual ~CMyData(){}int GetTag() const {return _tag;}std::string GetText() const {return _text;}private:int _tag;std::string _text;
};class CMyData_Child: public CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){// serialize base class informationar & boost::serialization::base_object<CMyData>(*this);ar & _number;}public:CMyData_Child():_number(0.0){}CMyData_Child(int tag, std::string text, float number):CMyData(tag, text), _number(number){}float GetNumber() const{return _number;}private:float _number;
};BOOST_CLASS_EXPORT_GUID(CMyData_Child, "CMyData_Child")class CMyData_Container
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){for(int i = 0; i < 3; i++){ar & pointers[i];}}
public:CMyData* pointers[3];
};void TestPointerArchive()
{std::string content;{CMyData d1(1, "a");CMyData_Child d2(2, "b", 1.5);CMyData_Container containter;containter.pointers[0] = &d1;containter.pointers[1] = &d2;containter.pointers[2] = &d1;std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << containter;content = os.str();}//·′DòáD?ˉ{CMyData_Container container;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> container;for (int i = 0; i < 3; i++){CMyData* d = container.pointers[i];std::cout << "pointer" << i + 1 <<": " << d->GetText() << "\n";if (i == 1){CMyData_Child* child = reinterpret_cast<CMyData_Child*>(d);std::cout << "pointer" << i + 1 <<", number: " << child->GetNumber() << "\n";}}}
}


這次我們可以正確的讀取到第二個指針指向的對象了,可以看到_number的爭取值了。

把代碼和上個版本想比較,我們會發現2個不同:

1. CMyData類里面多了個虛的析構函數;

2. 調用BOOST_CLASS_EXPORT_GUID給派生類CMyData_Child綁定一個字符串。

第二點很容易理解,就是給某個派生類命名一下,這樣就可以當作一個key來找到相應的類。那么第一點為什么要增加一個虛析構函數呢?是我無意中添加的嗎?當然不是,其實這個是序列化指向派生類的指針的其中一個關鍵。先看Boost網站上面的一段描述:

It turns out that the kind of object serialized depends upon whether the base class (base?in this case) is polymophic or not. Ifbase?is not polymorphic, that is if it has no virtual functions, then an object of the typebasewill be serialized. Information in any derived classes will be lost. If this is what is desired (it usually isn't) then no other effort is required.

If the base class is polymorphic, an object of the most derived type (derived_oneorderived_twoin this case) will be serialized.? The question of which type of object is to be serialized is (almost) automatically handled by the library.

ok,通過這段描述,我們發現Boost序列化庫會判斷基類是不是多態的。判斷的依據就是這個基類里面有沒有虛函數。我們知道,當一個類里面有虛函數的時候,C++編譯器會自動給這個類增加一個成員:_vfptr,就是虛函數表指針。我沒有花太多時間去看Boost有關這部分的源代碼,但是我猜測Boost是根據這個_vfptr來判斷是需要序列化基類,還是派生類的。我們增加一個虛析構函數的目的也就是讓CMyData產生一個_vfptr。我們可以試一下把上面的代碼里面的析構函數改成非虛的,那么派生類序列化就會失敗,跟上一個章節得到相同的結果。至于Boost怎么知道該序列化哪個派生類,相信這個是BOOST_CLASS_EXPORT_GUID的功勞,至于怎么實現,還是需要看源代碼,但是我自己沒有仔細研究過,有興趣的朋友可以學習Boost的源代碼。Boost的設計很巧妙,我們可以學到不少東西。當然這個得有時間細細學習。好了,序列化指向派生類指針就2個要點:

1. 讓Boost知道基類是多態的,其實就是確保基類里面有個虛函數;

2. 通過BOOST_CLASS_EXPORT_GUID給派生類綁定一個字符串,當作一個key。

?

至于第一種序列化指向派生類的基類指針:registration,可以參考http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/serialization.html#derivedpointers,上面講的非常清楚。我本人很少使用這種方式,這里也就略過不講了。

?

序列化數組

一個小細節,上面講到的序列化指針章節里面,我們看到代碼:

class CMyData_Container
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){for(int i = 0; i < 3; i++){ar & pointers[i];}}
public:CMyData* pointers[3];
};

其中的序列化函數里面有個for循環,難道每次序列化一個數組都需要弄一個for語句嗎,這個是不是可以改進呢?答案是肯定的。Boost自己會檢測數組。也就是說我們可以把代碼改成下面的形式:

class CMyData_Container
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & pointers;}
public:CMyData* pointers[3];
};

代碼短了很多,方便吧。
?

?支持STL容器

?上面我們使用了一個普通數組來保存指針,我相信在平常寫程序過程中,大家都會使用STL容器,比如list,map,array等等。至少我自己是經常使用的。那么Boost序列化庫可以序列化STL容器嗎?很幸運,Boost序列化庫已經支持了STL容器。原話是:

The above example uses an array of members.? More likely such an application would use an STL collection for such a purpose. The serialization library contains code for serialization of all STL classes.? Hence, the reformulation below will also work as one would expect.

?我們一開始就是用std::string作為CMyData的一個成員,我們不需要做任何工作就可以直接序列化std::string,這是因為Boost序列化庫已經支持std::string了。從上面的英文描述里面可以看到Boost serialization庫可以支持所有STL類,神奇吧。至少我本人經常使用std::list, vector, map, string等,都可以正常工作。下面的代碼使用std::vector代替了普通數組,可以正常工作。

class CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & _tag;ar & _text;}public:CMyData():_tag(0), _text(""){}CMyData(int tag, std::string text):_tag(tag), _text(text){}virtual ~CMyData(){}int GetTag() const {return _tag;}std::string GetText() const {return _text;}private:int _tag;std::string _text;
};class CMyData_Child: public CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){// serialize base class informationar & boost::serialization::base_object<CMyData>(*this);ar & _number;}public:CMyData_Child():_number(0.0){}CMyData_Child(int tag, std::string text, float number):CMyData(tag, text), _number(number){}float GetNumber() const{return _number;}private:float _number;
};BOOST_CLASS_EXPORT_GUID(CMyData_Child, "CMyData_Child")//ê1ó?STLèY?÷
class CMyData_ContainerSTL
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & vPointers;}
public:std::vector<CMyData*> vPointers;
};void TestPointerArchiveWithSTLCollections()
{std::string content;{CMyData d1(1, "parent obj");CMyData_Child d2(2, "child obj", 2.5);CMyData_ContainerSTL containter;containter.vPointers.push_back(&d1);containter.vPointers.push_back(&d2);containter.vPointers.push_back(&d1);std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << containter;content = os.str();}//·′DòáD?ˉ{CMyData_ContainerSTL container;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> container;std::cout<<"Test STL collections:\n";BOOST_FOREACH(CMyData* p, container.vPointers){std::cout << "object text: " << p->GetText() << "\n";CMyData_Child* child = dynamic_cast<CMyData_Child*>(p);if (child){std::cout << "child object number: " << child->GetNumber() << "\n";}}}
}

一不小心就用到了BOOST_FOREACH,看來這個確實很好用啊,呵呵。省去了寫很長的iterator來遍歷整個vector。

?

class版本
再來考慮一個問題,比方說現在我們程序升級了,然后把某個類給升級了一下,加了一個成員,那么之前保存的序列化的數據還能匹配到新的類嗎?看一下序列化函數,我們會發現這個序列化函數有個參數,叫做version

template<class Archive>
?void serialize(Archive& ar, const unsigned int version)

通過這個參數,我們就可以解決class版本的問題。看這段描述

In general, the serialization library stores a version number in the archive for each class serialized.? By default this version number is 0. When the archive is loaded, the version number under which it was saved is read.

也就是說如果我們不刻意指定version,那么Boost序列化庫就會默認設置為0并且保存到序列化結果中。

如果我們要標記不同的class版本,可以使用宏BOOST_CLASS_VERSION,比如

BOOST_CLASS_VERSION(CMyData, 1)

?具體這里就不舉例了。參考Boost說明。

?

save和load分開

?一直到現在我們都是用了一個序列化函數

template<class Archive>
?void serialize(Archive& ar, const unsigned int version)

其實,序列化包括序列化和發序列化兩部分,或者稱之為save和load,甚至mashalling,unmarshalling。反正就這個意思。

還有一個奇怪的地方,就是通常我們輸入輸出是用<<和>>的,那么在函數serialize里面我們用了&。其實這個是Boost對&做了一個封裝。假如現在是做序列化,那么&就等同于<<,假如是反序列化,那么&就等同于>>。然后序列化和反序列化統統用一個函數serialize來實現。這也體現了Boost的巧妙設計。

那么如果有特殊需求,我們需要把序列化和反序列化分開,應該怎么實現呢?

就好比上面的class版本問題,save和load可能就是不一樣的,因為load需要考慮兼容舊的版本。這里就偷懶使用Boost文檔上的例子了。我們可以看到save和load是分開的。

#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_member.hpp>class bus_route
{friend class boost::serialization::access;std::list<bus_stop *> stops;std::string driver_name;template<class Archive>void save(Archive & ar, const unsigned int version) const{// note, version is always the latest when savingar  & driver_name;ar  & stops;}template<class Archive>void load(Archive & ar, const unsigned int version){if(version > 0)ar & driver_name;ar  & stops;}BOOST_SERIALIZATION_SPLIT_MEMBER()
public:bus_route(){}
};BOOST_CLASS_VERSION(bus_route, 1)

注意需要使用宏BOOST_SERIALIZATION_SPLIT_MEMBER()來告訴Boost序列化庫使用save和load代替serialize函數。

到這里,我們幾乎把Boost序列化庫所有的內容都介紹完畢了。這個庫是相當的nice,基本可以cover所有的case。而且就開源庫來講,Boost的說明文檔真的算是很好的了。基本上都有詳細的說明,就序列化庫而言,直接看這個頁面就基本ok了,http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/tutorial.html?相當的詳細。盡管讀英文比較累,但是可以獲得原汁原味的第一手權威資料,花這些功夫還是值得的。

我的例子里面使用了二進制流來保存序列化后的數據,其實還有其他的archive格式,比如text,XML等等。甚至我們可以自己來實現序列化格式。Boost已經定義了一個統一的接口,我們要實現自己的格式,只需要繼承相應的Boost::archive里面的類就可以了。

好了,寫完了,希望對大家有點幫助。如有錯誤,歡迎指出。

?

附,完整測試代碼,使用這段代碼前需要確保VISUAL STUDIO已經設置了Boost的路徑。

// Serialization.cpp : Defines the entry point for the console application.
//#include "stdafx.h"#include "boost/serialization/serialization.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/archive/binary_iarchive.hpp"
#include <boost/serialization/export.hpp>
#include "boost/foreach.hpp"
#include "boost/any.hpp"
#include <boost/serialization/vector.hpp>#include <string>
#include <Windows.h>
#include <iostream>
#include <sstream>
#include <vector>//測試序列化
class CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & _tag;ar & _text;}public:CMyData():_tag(0), _text(""){}CMyData(int tag, std::string text):_tag(tag), _text(text){}virtual ~CMyData(){}int GetTag() const {return _tag;}std::string GetText() const {return _text;}private:int _tag;std::string _text;
};void TestArchive1()
{std::string content;{CMyData d1(2012, "China, good luck");std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面content = os.str();//content保存了序列化后的數據。}{CMyData d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData tag: " << d2.GetTag() << ", text: " << d2.GetText() << "\n";}}class CMyData2
{
public:CMyData2():_tag(0), _text(""){}CMyData2(int tag, std::string text):_tag(tag), _text(text){}int _tag;std::string _text;
};namespace boost {namespace serialization {template<class Archive>void serialize(Archive & ar, CMyData2 & d, const unsigned int version){ar & d._tag;ar & d._text;}} // namespace serialization
} // namespace boostvoid TestArchive2()
{CMyData2 d1(2012, "China, good luck");std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData2 d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData2 tag: " << d2._tag << ", text: " << d2._text << "\n";
}//序列化子類,侵入式
class CMyData_Child: public CMyData
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){// serialize base class informationar & boost::serialization::base_object<CMyData>(*this);ar & _number;}public:CMyData_Child():_number(0.0){}CMyData_Child(int tag, std::string text, float number):CMyData(tag, text), _number(number){}float GetNumber() const{return _number;}private:float _number;
};BOOST_CLASS_EXPORT_GUID(CMyData_Child, "CMyData_Child")void TestArchive3()
{CMyData_Child d1(2012, "China, good luck", 1.2);std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData_Child d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData_Child tag: " << d2.GetTag() << ", text: " << d2.GetText() << ", number: "<<d2.GetNumber() << "\n";
}//序列化子類,非侵入式
class CMyData2_Child: public CMyData2
{
public:CMyData2_Child():_number(0.0){}CMyData2_Child(int tag, std::string text, float number):CMyData2(tag, text), _number(number){}float _number;
};namespace boost {namespace serialization {template<class Archive>void serialize(Archive & ar, CMyData2_Child & d, const unsigned int version){// serialize base class informationar & boost::serialization::base_object<CMyData2>(d);ar & d._number;}} // namespace serialization
} // namespace boostvoid TestArchive4()
{CMyData2_Child d1(2012, "test non-intrusive child class", 5.6);std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << d1;//序列化到一個ostringstream里面std::string content = os.str();//content保存了序列化后的數據。CMyData2_Child d2;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> d2;//從一個保存序列化數據的string里面反序列化,從而得到原來的對象。std::cout << "CMyData2_Child tag: " << d2._tag << ", text: " << d2._text << ", number: "<<d2._number<<"\n";
}//指針數據成員class CMyData_Container
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & pointers;}
public:CMyData* pointers[3];
};void TestPointerArchive()
{std::string content;{CMyData d1(1, "a");CMyData_Child d2(2, "b", 1.5);CMyData_Container containter;containter.pointers[0] = &d1;containter.pointers[1] = &d2;containter.pointers[2] = &d1;std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << containter;content = os.str();}//反序列化{CMyData_Container container;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> container;for (int i = 0; i < 3; i++){CMyData* d = container.pointers[i];std::cout << "pointer" << i + 1 <<": " << d->GetText() << "\n";if (i == 1){CMyData_Child* child = reinterpret_cast<CMyData_Child*>(d);std::cout << "pointer" << i + 1 <<", number: " << child->GetNumber() << "\n";}}}
}//使用STL容器
class CMyData_ContainerSTL
{
private:friend class boost::serialization::access;template<class Archive>void serialize(Archive& ar, const unsigned int version){ar & vPointers;}
public:std::vector<CMyData*> vPointers;
};void TestPointerArchiveWithSTLCollections()
{std::string content;{CMyData d1(1, "parent obj");CMyData_Child d2(2, "child obj", 2.5);CMyData_ContainerSTL containter;containter.vPointers.push_back(&d1);containter.vPointers.push_back(&d2);containter.vPointers.push_back(&d1);std::ostringstream os;boost::archive::binary_oarchive oa(os);oa << containter;content = os.str();}//反序列化{CMyData_ContainerSTL container;std::istringstream is(content);boost::archive::binary_iarchive ia(is);ia >> container;std::cout<<"Test STL collections:\n";BOOST_FOREACH(CMyData* p, container.vPointers){std::cout << "object text: " << p->GetText() << "\n";CMyData_Child* child = dynamic_cast<CMyData_Child*>(p);if (child){std::cout << "child object number: " << child->GetNumber() << "\n";}}}
}int _tmain(int argc, _TCHAR* argv[])
{TestArchive1();TestArchive2();TestArchive3();TestArchive4();TestPointerArchive();TestPointerArchiveWithSTLCollections();return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536186.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536186.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536186.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java基礎經典練習題

【程序1】 題目&#xff1a;古典問題&#xff1a;有一對兔子&#xff0c;從出生后第3個月起每個月都生一對兔子&#xff0c;小兔子長到第三個月后每個月又生一對兔子&#xff0c;假如兔子都不死&#xff0c;問每個月的兔子總數為多少&#xff1f; //這是一個菲波拉契數列問題 p…

ubuntu下wps不能輸入中文

ubuntu下wps不能輸入中文 原因是因為fcitx環境的原因&#xff0c;想了解fcitx的可以看這篇文章&#xff0c;鏈接。 使用腳本解決 將下面的腳本復制到新建的文件中&#xff0c;chmod加權限&#xff0c;然后執行即可。 #! /bin/bash #--------------------------------------…

常見的幾種內排序算法以及實現(C語言)(轉)

所有未排序的數組是經過檢查合法的主要的內排序包括冒泡、插入、希爾、堆排序、歸并、快速、桶排序等其C語言實現的源文件下載地址&#xff1a;http://download.csdn.net/detail/mcu_tian/9530227冒泡排序冒泡排序應該是排序中最簡單的算法了主要思路如下&#xff1a;1&#xf…

常見編程命名縮寫

命名縮寫 通用縮寫翻譯控件縮寫翻譯addressaddr地址calendarcdr日歷applicationapp應用程序messageDialogmsgdlg消息框asynchronizationasyn異步drawerdrw抽屜averageavg平均數buttonGroupbtngrp按鈕分組bitmapbmp位圖checkBoxchk復選框bufferbuf緩沖區containercntr容器chara…

funCode課程實訓(C++ )

funcode是一個簡單的游戲制作引擎&#xff0c;適合c初學者操作&#xff0c;可以幫助初學者更好的了解c環境&#xff0c;以及各種函數的實現&#xff0c;本學期我們用funcode作為C最后的課程設計&#xff0c;所以我就使用funcode制作一個打地鼠的小游戲。以下是對這個小程序的描…

Nodejs,Npm,React安裝教程

React安裝 1.下載node.js安裝包 下載二進制包 選擇比較穩定的版本進行安裝&#xff0c;v8.9 2.安裝 直接把文件解壓復制到某個目錄下&#xff0c; sudo cp -r node-v8.9.0 /opt/node #你下載的版本sudo touch /etc/profile.d/node.sh #新建一個腳本文件sudo gedit /etc/…

Ubuntu下的提示信息彩色顯示

【問題】 雖然已經折騰過了&#xff1a; 【已解決】Ubuntu中讓終端只顯示當前路徑&#xff0c;而不顯示絕對路徑 但是&#xff0c;終端中的prompt提示信息&#xff0c;不是彩色的&#xff0c;導致的結果是&#xff1a; 當終端中輸出信息很多時&#xff1a; 【已解決】Ubun…

hustoj的搭建

最近開始接觸服務器之類的&#xff0c;就自己搭建一個hustoj的服務器&#xff0c;hustoj系統的搭建在網上已經很完善了&#xff0c;這里我就簡單的說一下&#xff0c;作為自己的學習筆記。 安裝主要環境&#xff0c;Apache2&#xff0c;MySQL&#xff0c;php5和PHPmyadmin。 …

Shell字符串操作集合

字符操作字符串的長度獲取字符串中某些字符的個數統計單詞的個數bash提供的數組數據結構它是以數字為下標的和C語言從0開始的下一樣awk里面的數組取子串匹配求子串sed有按行打印的功能記得用tr把空格換為行號tr來取子串head和tail查詢字串子串替換tac 會將文本的內容倒置顯示正…

百練4982 踩方格

總時間限制: 1000ms 內存限制: 65536kB描述有一個方格矩陣&#xff0c;矩陣邊界在無窮遠處。我們做如下假設&#xff1a;a. 每走一步時&#xff0c;只能從當前方格移動一格&#xff0c;走到某個相鄰的方格上&#xff1b;b. 走過的格子立即塌陷無法再走第二次&#xff1b;…

Qt自定義QML模塊

自定義QML模塊 含義為將常用風格的Button&#xff0c;Text,RadioButton,或者自定義的控件作為一個控件進行使用&#xff0c;節省代碼。 優點&#xff1a; 代碼簡潔&#xff0c;減少重復代碼自定義的控件進行封裝重復使用可以與QML自帶的庫區別開來優化項目結構 一、創建模塊…

POJ3984 迷宮問題【BFS】

好長時間沒有敲過代碼了&#xff0c;感覺之前學過的都忘了&#xff0c;趁著這個暑假&#xff0c;打算把之前學習的東西都復習一下&#xff0c;當然得慢慢來&#xff0c;畢竟好長時間不敲代碼了&#xff0c;怎么著都有些生疏&#xff0c;再加上之前學的也不咋地&#xff0c;相當…

宏定義基本用法

宏定義 不帶參數 宏定義又稱為宏代換、宏替換&#xff0c;簡稱“宏”。 格式&#xff1a; #define 標識符 字符串其中的標識符就是所謂的符號常量&#xff0c;也稱為“宏名”。 預處理&#xff08;預編譯&#xff09;工作也叫做宏展開&#xff1a;將宏名替換為字符串。 掌…

廣度優先搜索練習之神奇的電梯

廣度優先搜索練習之神奇的電梯 Time Limit: 1000ms Memory limit: 65536K 題目描述 有一座已知層數為n的高樓&#xff0c;這座高樓的特殊之處在于只能靠電梯去上下樓&#xff0c;所以要去到某一層要非常耽誤時間&#xff0c;然而更悲哀的是&#xff0c;這座高樓的電梯是限號…

ubuntu安裝proxychains及自動補全

proxychains ProxyChains是本人目前為止用到的最方便的代理工具。 inux下代理一般是通過http_proxy和https_proxy這兩個環境變量&#xff0c;但是很多軟件并不使用這兩個變量&#xff0c;導致流量無法走代理。在不使用vpn的前提下&#xff0c;linux并沒有轉發所有流量的真全局…

快速冪講解

快速冪的目的就是做到快速求冪&#xff0c;假設我們要求a^b,按照樸素算法就是把a連乘b次&#xff0c;這樣一來時間復雜度是O(b)也即是O(n)級別&#xff0c;快速冪能做到O(logn)&#xff0c;快了好多好多。它的原理如下&#xff1a; 假設我們要求a^b&#xff0c;那么其實b是可以…

如何查詢資料

如何查詢資料技術資料及問題查詢查詢方法分類查找提取關鍵字GitHub項目優先使用Google搜索引擎Copy Paste論文查找詢問主管 測試修改使用總結分享 公司信息查詢國內公司國外公司 如何查詢資料 技術資料及問題查詢 查詢方法 資料與解決辦法的查詢大致分為7大類。 1.分類查…

山東省第八屆 ACM 省賽 sum of power(SDUT 3899)

Problem Description Calculate ∑ni1im mod (10000000007) for given n&#xff0c;m. Input Input contains two integers n,m(1≤n≤1000,0≤m≤10). Output Output the answer in a single line. Example Input 10 0 Example Output 10 方法&#xff1a;快速冪和大數求和 …

Ubuntu主題更換

Ubuntu主題更換 目前的Ubuntu有Unity和Gnome兩個比較流行的版本&#xff0c;以下為Gnome桌面環境的主題更換&#xff0c;其他桌面環境類似。 主題的下載地址&#xff0c;點擊 Theme 將在網絡上下載的主題文件進行解壓&#xff0c;然后拷貝到 /usr/share/themes/ 目錄下&…

awk簡單使用

awk 用于在linux/unix下對文本和數據進行處理,支持用戶自定義函數和動態正則表達式等先進功能。 命令格式&#xff1a; awk BEGIN{ print “start” } pattern { commend } END{print "end"} file awk "BEGIN{ print “start” } pattern { commend } END{pr…