• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            序列化

             

            為何需要序列化

            我們常常需要將對象持久化或者在通信中傳遞對象,這時我們就會使用到序列化。

            C++中的序列化

            對于C++這樣的靜態語言來說,序列化往往都很不方便,它對于類型轉換非常敏感和嚴格,所以說想要對C++對象進行序列化那必須對每個序列化的對象進行針對性編碼。

            C++中的反序列化

            因為C++類型是在編譯時決定的,而序列化是運行時行為,反序列化需要生成的對象類型必須在編譯時決定好,這讓人感到很無力。為了能夠正確的進行反序列化,我們通常會生成一個映射表,將對象的類型所對應的ID一并序列化,這樣就能在運行時通過”預反序列化“得到類型ID,然后查找映射表來得到需要創建的對象類型,然后創建對象進行反序列化。

            序列化類型映射表

            理想的序列化類型映射表能夠在編譯時生成,因為我們需要序列化的類型都是編譯時決定好了的,所以在編譯時生成其映射表應該理所當然。然而非常尷尬的是,C++如何建立類型ID到類型的映射?它是靜態類型的語言,如果要建立一個類型的映射表,即使使用模板技術也只能做到編譯期映射(也就是需要用編譯時常量進行類型查找,對于運行時的反序列化行為,這顯然是沒有任何作用的)。

            一個可行的方案

            為了能夠在運行時進行查找,我使用了反序列化輔助對象作為映射類型的載體。這個對象利用模板來關聯序列化類型,它同時也要求序列化類型需要提供默認構造,并且默認構造能夠被其所訪問。

            class SerializableObject;

            class UnserializerBase{
            protected:
            UnserializerBase(){}
            public:
            virtual ~UnserializerBase(){}

            public:
            virtual SerializableObject* Unserialize(const char*& pData) = 0;
            };

            template
            <typename T>
            class Unserializer : public UnserializerBase{
            public:
            typedef T Object;

            public:
            Unserializer(){}

            public:
            SerializableObject
            * Unserialize(const char*& pData)
            {
            Object
            * pObj = new Object; // 要求需要序列化支持的類具有默認構造函數
            pData += pObj->Unserialize(pData);
            return pObj;
            }
            };

            這里顯而易見的是我們的序列化對象必須從SerializableObject派生。

            類型ID的計算

            簡單起見,我們可以通過C++提供的typeid來計算類型的ID,當然這需要開啟相應編譯開關。序列化類型可以通過模板參數將自身傳遞給某個模板函數來計算其ID,并將這個ID和反序列化輔助類型Unserializer對象建立起映射關系。

            template<typename Object>
            static ClassIDType BuildClassID()
            {
            static Unserializer<Object> unserializer;
            static std::string name(typeid(Object).name());
            static ClassIDType id = std::hash<BinaryData>()(BinaryData(name.c_str(), name.length()));
            GetIDMapSerializer()[id]
            = &unserializer;
            return id;
            }



            static std::map<ClassIDType, UnserializerBase*>& GetIDMapSerializer()
            {
            static std::map<ClassIDType, UnserializerBase*> id_unserializer;
            return id_unserializer;
            }
            此處的實現對類型名求了hash值來作為類型ID,至于碰撞的概率和我們的序列化類型數量相比較就可以忽略不計了。并且創建的都是靜態對象,這能夠保證每個序列化類型值生成一次類型ID和反序列化輔助對象。

            類型ID的序列化和反序列化

            序列化對象對于其ID的序列化和反序列化時的查找過程并不關心,那么我們應該透明的去完成這個過程。同時考慮到序列化對象需要將自身類型進行注冊,我們可以在此時就完成我們的類型ID到反序列化輔助對象的映射的創建。我們在SerializableObject基類和序列化類型之間插入了一個中間模板類,它完成我們剛才提到的映射的創建以及ID的序列化工作(后面講為什么它不完成ID反序列化的工作)。

            // 派生類將自身類型傳遞給模板參數
            template<typename T>
            class Serializable : public SerializableObject{
            protected:
            static const ClassIDType ID;

            public:
            typedef T Object;

            protected:
            Serializable(){ID;}

            public:
            virtual ~Serializable(){}

            public:
            template
            <typename BufferType>
            void Serialize(BufferType& buffer)
            {
            SaveToBuffer(ID, buffer);
            // 這里應該判斷子類序列化是否成功,如果不成功則回滾,暫時未加此功能

            static_cast
            <Object*>(this)->DoSerialize_(buffer);
            }
            size_t Unserialize(
            const char* pData)
            {
            return DoUnserialize_(pData);
            }
            static const ClassIDType GetClassID(){return ID;}

            private:
            // 通過模板模擬虛函數
            template<typename BufferType>
            void DoSerialize_(BufferType& buffer){};
            virtual size_t DoUnserialize_(const char* pData) = 0;
            };

            template
            <typename T>
            const typename Serializable<T>::ClassIDType Serializable<T>::ID = Serializable<T>::BuildClassID<T>();

            這里為了能夠減少對存放序列化內容的緩沖區的依賴,我們對序列化接口使用了模板推導,但是這導致我們難以通過虛函數來延遲序列化實現。這里我們采用了一個非標準的實現方式——子類注冊自身類型給父類,并要求子類必須實現一個名為DoSerialize_的模板成員函數,那么我們就可以通過類型轉換去調用子類接口了,達到類似虛函數的功能。

            我們最希望的反序列化應該是這樣的:

            Type* pObject = dynamic_cast<Type*>(Unserialize(pData));
            

            那么,反序列化接口就不能作為一個模板類的靜態函數。所以我們將這個接口放到了SerializableObject當中

            // 此類實現的靜態方法Create用于反序列化
            class SerializableObject{
            public:
            typedef size_t ClassIDType;

            protected:
            SerializableObject(){}

            public:
            virtual ~SerializableObject(){}

            public:
            static SerializableObject* Create(const char*& pData)
            {
            ClassIDType id
            = 0;
            pData
            += LoadFromData(id, pData);
            std::map
            <ClassIDType, UnserializerBase*>& id_serializer = GetIDMapSerializer();
            auto iter
            = id_serializer.find(id);
            if (id_serializer.end() != iter)
            {
            return (*iter).second->Unserialize(pData);
            }
            return 0;
            }

            protected:
            template
            <typename Object>
            static ClassIDType BuildClassID()
            {
            static Unserializer<Object> unserializer;
            static std::string name(typeid(Object).name());
            static ClassIDType id = std::hash<BinaryData>()(BinaryData(name.c_str(), name.length()));
            GetIDMapSerializer()[id]
            = &unserializer;
            return id;
            }

            private:
            static std::map<ClassIDType, UnserializerBase*>& GetIDMapSerializer()
            {
            static std::map<ClassIDType, UnserializerBase*> id_unserializer;
            return id_unserializer;
            }
            };

            這樣,基本上我們就可以這樣來定義我們的序列化類型了:

            class Test
            :
            public Serializable<Test>
            {
            int num_;

            public:
            explicit Test(int n = 0) : num_(n){}

            private:
            friend
            class Serializable<Test>;
            template
            <typename BufferType>
            void DoSerialize_(BufferType& buffer)
            {
            SaveToBuffer(num_, buffer);
            }

            size_t DoUnserialize_(
            const char* pData)
            {
            return LoadFromData(num_, pData);
            }
            };

            這里的SaveToBuffer和LoadFromData是一對序列化幫助函數。

            這個序列化和反序列化的實現有個很大的缺點,那就是映射的建立。因為我們使用的是模板,所以說序列化類型不被使用就不會被編譯,那么這個類型就不會被加入到映射中,那么我們的程序就可能出現問題(找不到某個ID所映射的反序列化輔助對象)。解決這個問題的方法就是強制編譯器編譯我們可能會使用到的序列化類型,比如為每個序列化類型定義一個其類型ID的靜態引用,如果是程序中已經使用到序列化類型的話則不必。

            示例測試:

            假設我們有一套命令系統,各種不同的命令派生自一個命令基類Command,并實現其名為Execute的接口。我們需要將不同的命令類型對象序列化和反序列化(這在網絡通信協議中普遍使用,網絡兩端可定義相同的命令類型,一端只對這個命令對象存儲參數,一端負責實現其執行過程),反序列化過程中不需要關心具體的命令類型,只需要調用其接口Execute便可。

            我們的序列化和反序列化非常適合這個系統,下面就給出測試工程代碼,測試工程沒有建立網絡通信,只是體現我們的序列化和反序列化的方式。

            下載測試工程源碼


            作者: Evil.Ghost 發表于 2011-04-04 19:58 原文鏈接

            評論: 0 查看評論 發表評論


            最新新聞:
            · 蘋果iPad 2通過3C認證 最晚5月國內上市(2011-04-10 09:18)
            · 盲目依賴iPhone等工具導航 英國驢友迷路多(2011-04-10 09:14)
            · 趣談:想擔任CEO的話,最好是去蘋果工作,其次是微軟,再才是Google(2011-04-10 08:26)
            · 騰訊將建立新數據中心,規模為蘋果的兩倍(2011-04-10 08:25)
            · 輕量化的微型博客Tumblr(2011-04-10 08:03)

            編輯推薦:非戰之罪,從永中Office談起

            網站導航:博客園首頁  我的園子  新聞  閃存  小組  博問  知識庫

            posted on 2011-04-04 19:58 EvilGhost 閱讀(824) 評論(0)  編輯 收藏 引用

            導航

            統計

            常用鏈接

            留言簿

            隨筆檔案(12)

            文章檔案(1)

            最新隨筆

            搜索

            積分與排名

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

            性色欲网站人妻丰满中文久久不卡| 亚洲精品无码久久毛片| 天天躁日日躁狠狠久久| 少妇内射兰兰久久| 热久久这里只有精品| 日韩亚洲国产综合久久久| 国内精品久久久久影院免费| 狠狠精品干练久久久无码中文字幕| 久久久久99精品成人片三人毛片| 亚洲国产精品无码久久98| 久久精品视频网| 亚洲国产欧美国产综合久久| 一本一道久久精品综合| 久久亚洲日韩看片无码| 91精品日韩人妻无码久久不卡 | 热re99久久6国产精品免费| 国产成人久久激情91| 青草久久久国产线免观| AAA级久久久精品无码片| 亚洲欧美日韩精品久久亚洲区 | 伊人久久综在合线亚洲2019| 久久精品国产免费观看 | 99精品久久精品一区二区| 久久99精品国产麻豆婷婷| 97精品久久天干天天天按摩| 中文字幕久久久久人妻| 合区精品久久久中文字幕一区| 婷婷久久综合九色综合98| 狠狠色婷婷久久一区二区三区| 精品久久久无码21p发布| 蜜臀久久99精品久久久久久| 久久国产免费| 久久国产三级无码一区二区| 91久久精品电影| 精品99久久aaa一级毛片| 久久精品二区| 久久人人超碰精品CAOPOREN| 久久精品成人一区二区三区| 久久夜色精品国产| 久久大香萑太香蕉av| 亚洲日本va中文字幕久久|