C++中將對象進行序列化的經常使用方法有三種:protobuf、 Boost.Serializatio、MFC Serialization。編程
根據使用場景,各有優點網絡
- protobuf :須要單獨定義格式,通常用於網絡通訊,用於數據包的傳輸。
- Boost.Serializatio : 可以建立或重建程序中的等效結構,並保存爲二進制數據、文本數據、XML或者實用戶本身定義的其它文件
- MFC Serialization: MFC 對 CObject 類中的序列化提供內置支持
對於如下小型項目,若是須要保持程序中的等效結構,但又不想引入boost庫的話,其實也能夠自行實現,基於二進制的序列化與反序列化並不難,下面將詳細介紹實現過程,支持C++基礎類類型及stl容器類型。測試
測試效果:spa
void print(const std::string& msg) { //反序列化 std::istringstream iss(msg); std::vector<std::string> d; Deserialize(iss, d); for(auto item : d) { std::cout << item << std::endl; } int val; Deserialize(iss, val); std::cout << "-->" << val << std::endl; std::string str; Deserialize(iss, str); std::cout << "-->" << str << std::endl; std::list<int> s; Deserialize(iss, s); for (auto item : s) { std::cout << item << std::endl; } std::map<std::string, int> m; Deserialize(iss, m); for (auto item : m) { std::cout << item.first << "-->" << item.second << std::endl; } std::tuple<int, std::string, double> tp; Deserialize(iss, tp); std::cout << std::get<0>(tp) << "-->" << std::get<1>(tp) << "-->" << std::get<2>(tp) << std::endl; std::set<int> st; Deserialize(iss, st); for (auto item : st) { std::cout << item << std::endl; } std::vector<MyTest> vecVal; Deserialize(iss, vecVal); for(auto item : vecVal) { std::cout << "age:" << *item.p << " name:" << item.name << std::endl; } } int main() { //序列化 std::vector<std::string> d = { "10","20","50","100","1000" }; std::ostringstream oss; Serialize(oss, d); Serialize(oss, 10); std::string str("hello"); Serialize(oss, str); std::list<int> s = { 10,20,50,100,1000 }; Serialize(oss, s); std::map<std::string, int> val = { { "1",3 }, { "3",4 }, {"6",8} }; Serialize(oss, val); std::tuple<int, std::string, double> tp{ 0, "test", 1.3 }; Serialize(oss, tp); std::vector<MyTest> vecVal = {MyTest(35, "hello"), MyTest(40, "world")}; std::set<int> st = { 100,30,70 }; Serialize(oss, st); Serialize(oss, vecVal); print(oss.str()); return 0; }
輸出:code
實現過程對象
利用C++模板能夠大大簡化處理過程,第一步咱們須要對C++經常使用類型進行分類,而後結合C++泛型編程分步實現。blog
經常使用類型分類:內存
一、可平凡複製類型 (C++ POD概念,支持按位複製的類型)get
二、std::string 最經常使用類型,雖然string是能夠自動分配內存的,但能夠針對連續內存的特性進行特化處理。string
三、容器類型,須要支持容器嵌套處理
四、std::pair類型,多值結構須要特殊處理
五、std::tuple 元組不一樣於其它C++容器,須要特殊處理。
如下位序列化代碼:
//可平凡複製 template <typename T, typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type N = 0> void Serialize(std::ostream & os, const T & val) { os.write((const char *)&val,sizeof(T)); } //pair template <typename K, typename V> void Serialize(std::ostream & os, const std::pair<K, V> & val) { Serialize(os, val.first); Serialize(os, val.second); } // std::string void Serialize(std::ostream & os, const std::string &val) { size_t size = val.size(); os.write((const char *)&size, sizeof(size)); os.write((const char *)val.data(), size * sizeof(typename std::string::value_type)); } //容器 template <typename T, typename std::enable_if< std::is_same<typename std::iterator_traits<typename T::iterator>::value_type, typename T::value_type>::value , int>::type N = 0> void Serialize(std::ostream & os, const T & val) { size_t size = val.size(); os.write((const char *)&size, sizeof(size_t)); for (auto & v : val) { Serialize(os, v); } } //tuple template<typename T> int _Serialize(std::ostream & os, T& val) { Serialize(os, val); return 0; } template<typename Tuple, std::size_t... I> void _Serialize(std::ostream & os, Tuple& tup, std::index_sequence<I...>) { std::initializer_list<int>{_Serialize(os, std::get<I>(tup))...}; } template <typename...Args> void Serialize(std::ostream & os, const std::tuple<Args...>& val) { _Serialize(os, val, std::make_index_sequence<sizeof...(Args)>{}); }
反序列化
// 可平凡複製 template <typename T,typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type N = 0> void Deserialize(std::istream & is, T & val) { is.read((char *)&val, sizeof(T)); } // std::string void Deserialize(std::istream & is, std::string &val) { size_t size = 0; is.read((char*)&size, sizeof(size_t)); val.resize(size); auto count = size * sizeof(typename std::string::value_type); is.read((char *)val.data(), count); } //pair template <typename V> struct ImpDeserialize { template<typename T> static void get(std::istream & is, T & val) { size_t size = 0; is.read((char *)&size, sizeof(size_t)); for (size_t i = 0; i < size; i++) { V v; Deserialize(is, v); val.insert(val.end(), v); } } }; template <typename K, typename V> struct ImpDeserialize<std::pair<K, V>> { template<typename T> static void get(std::istream & is, T & val) { size_t size = 0; is.read((char *)&size, sizeof(size_t)); for (size_t i = 0; i < size; i++) { K k; V v; Deserialize(is, k); Deserialize(is, v); val.emplace(k, v); } } }; // 容器 template <typename T, typename std::enable_if< std::is_same<typename std::iterator_traits<typename T::iterator>::value_type, typename T::value_type>::value , int>::type N = 0> void Deserialize(std::istream & is, T & val) { ImpDeserialize<typename T::value_type>::get(is, val); } //tuple template<typename T> int _Deserialize(std::istream & is, T& val) { Deserialize(is, val); return 0; } template<typename Tuple, std::size_t... I> void _Deserialize(std::istream & is, Tuple& tup, std::index_sequence<I...>) { std::initializer_list<int>{_Deserialize(is, std::get<I>(tup))...}; } template <typename...Args> void Deserialize(std::istream & is, std::tuple<Args...>& val) { _Deserialize(is, val, std::make_index_sequence<sizeof...(Args)>{}); }