C++基本功之 对象序列化
2012-11-28 13:21
169 查看
稍微正式一点的应用都会用到对象的序列化/反序列化操作,常见的需求包括: 对象的持久存储。比如把一个对象存储到文件;在需要的时候,再把对象从文件中读出来; 对象的传递。比如要把一个对象通过管道,socket等任何手段传送到对端; 从数据构建对象。对象类型未知,但是我们可以从数据中构建一个对象出来。 我们现在来分析这些需求。看看C++如何有效地序列化/反序列化对象。下文中,凡提到序列化,都包含相应的反序列化。 一个对象的状态由其所有的内部成员状态决定。所以序列化对象其实可以等价于序列化其内部对象,递归地,其内部对象的序列化又可以归于构成这些内部对象的序列化。我们可以发现,到了最后,所有的对象构成都归于基本类型的序列化。 注意,这里的讨论并不适用于任何的资源管理类对象。 首先,我们需要的是最基本的支持,亦即对C++基本类型的序列化和反序列化;同时,由于标准库中的字符串类使用极为广泛,我们同样加入支持;最后,我们也加入对非格式的POD数据的支持,这样,据大多数的序列化需求应该可以得到满足。以下直接给出实现,其后是简单的说明,请仔细揣摩。 [cpp] view plaincopy namespace persistent { const long true_pattern = 0xF00DF00D; const long false_pattern = 0xBAD0BAD0; class serializer { public: serializer (std::ostream& os) : _ostream(os) {} void put_char(char value) { return put_basic_t<char>(value); } void put_wchar(wchar_t value) { return put_basic_t<wchar_t>(value); } void put_bool(bool value) { return put_basic_t<bool>(value); } void put_short(short value) { return put_basic_t<short>(value); } void put_long(long value) { return put_basic_t<long>(value); } void put_llong(int64 value) { return put_basic_t<int64>(value); } void put_double(double value) { return put_basic_t<double>(value); } void put_string(const std::string& value) { return put_string_t<std::string>(value); } void put_wstring(const std::wstring& value) { return put_string_t<std::wstring>(value); } void put_raw(size_t length, const byte* buff) { put_long(static_cast<long>(length)); _ostream.write(reinterpret_cast<const char*>(buff), static_cast<std::streamsize>(length)); if (_ostream.bad()) throw std::exception("stream_write"); } void put_raw(const util::raw_buffer& buff) { _ostream.write(reinterpret_cast<const char*>(buff.operator const byte*()), static_cast<std::streamsize>(buff.size())); if (_ostream.bad()) throw std::exception("stream_write"); } protected: template<typename T> void put_basic_t(T value) { _ostream.write (reinterpret_cast<char*>(&value), sizeof(T)); if (_ostream.bad()) throw std::exception("stream_write"); } template<> void put_basic_t(bool value) { long pattern = value? true_pattern: false_pattern; put_long(pattern); if (_ostream.bad()) throw std::exception("stream_write"); } template<typename T> void put_string_t(const T& value) { long len = static_cast<long>(value.length() * sizeof(T::value_type)); put_long(len); _ostream.write(reinterpret_cast<const char*>(value.data()), len); if (_ostream.bad()) throw std::exception("stream_write"); } private: std::ostream& _ostream; serializer& operator =(const serializer&); }; class deserializer { public: deserializer (std::istream& is) : _istream(is) {} char get_char() { return get_basic_t<char>(); } wchar_t get_wchar() { return get_basic_t<wchar_t>(); } bool get_bool() { return get_basic_t<bool>(); } short get_short() { return get_basic_t<short>(); } long get_long() { return get_basic_t<long>(); } int64 get_llong() { return get_basic_t<int64>(); } double get_double() { return get_basic_t<double>(); } std::string get_string() { return get_string_t<std::string>(); } std::wstring get_wstring() { return get_string_t<std::wstring>(); } util::raw_buffer get_raw() { size_t length = static_cast<size_t>(get_long()); if (_istream.eof()) throw std::exception("unexpected_eof"); util::raw_buffer value(length); _istream.read(value.force_to<char*>(), static_cast<std::streamsize>(length)); if (_istream.bad()) throw std::exception("stream_read"); return value; } void get_raw(util::raw_buffer& buff) { if (_istream.eof()) throw std::exception("unexpected_eof"); _istream.read(buff.force_to<char*>(), static_cast<std::streamsize>(buff.size())); if (_istream.bad()) throw std::exception("stream_read"); } protected: template <typename T> T get_basic_t() { if (_istream.eof()) throw std::exception("unexpected_eof"); T value; _istream.read(reinterpret_cast<char *>(&value), sizeof(T)); if (_istream.bad()) throw std::exception("stream_read"); return value; } template<> bool get_basic_t() { long value = get_long(); if (_istream.bad()) throw std::exception("stream_read"); if (value == true_pattern) return true; else if (value == false_pattern) return false; else throw std::exception("data_corrupt"); } template<typename T> T get_string_t() { long len = get_long(); T value; value.resize(len / sizeof(T::value_type)); if (_istream.eof()) throw std::exception("unexpected_eof"); _istream.read(reinterpret_cast<char*>(&value[0]), len); if (_istream.bad()) throw std::exception("stream_read"); return value; } private: std::istream& _istream; deserializer& operator =(const deserializer&); }; } 说明: 1. 使用独立的名字空间,避免名字冲突; 2. 对于bool型,理论上使用一个bit就可以表达其所有的信息。但是我们实际使用还是一个字节,因为这是内存操作的最小单位。为了有效地利用额外的信息,我们这里加入了对bool的校验,使用两个特殊的值;这两个值可以是任意值,只要不同即可。 3. 实现两个类,分别完成基本对象,字符串及其宽字符版本以及POD数据的序列化和反序列化; 4. 使用成员模板来消除基本对象序列化/反序列话过程中的代码重复; 5. 对于I/O操作失败,我们直接抛出异常。注意,这不是临时方案。而是经过仔细设计的序列化/反序列化过程的错误处理机制; 6. 构造这些对象需要输出或输入流对象,而不仅仅限于文件; 有了这个基础,我们就可以设计通用的序列化/反序列化接口了,非常简单: [cpp] view plaincopy namespace persistent { class serializable { public: virtual void serialize(serializer& out) const = 0; virtual void deserialize(deserializer& in) = 0; }; } 那么,这个接口怎么使用呢?假设你有如下类,如果实现对其序列化/反序列化的支持呢? [cpp] view plaincopy struct mydata { char _c; wchar_t _w; bool _b; short _s; long _l; int64 _64; double _d; std::string _str; std::wstring _wstr; }; 下面就是实现代码以及用来测试的代码: [cpp] view plaincopy struct mydata : public serializable { char _c; wchar_t _w; bool _b; short _s; long _l; int64 _64; double _d; std::string _str; std::wstring _wstr; public: virtual void serialize(serializer& out) const { out.put_char(_c); out.put_wchar(_w); out.put_bool(_b); out.put_short(_s); out.put_long(_l); out.put_llong(_64); out.put_double(_d); out.put_string(_str); out.put_wstring(_wstr); } virtual void deserialize(deserializer& in) { _c = in.get_char(); _w = in.get_wchar(); _b = in.get_bool(); _s = in.get_short(); _l = in.get_long(); _64 = in.get_llong(); _d = in.get_double(); _str = in.get_string(); _wstr = in.get_wstring(); } bool operator == (const mydata& r) { return _c == r._c && _w == r._w && _b == r._b && _s == r._s && _l == r._l && _64 == r._64 && _d == r._d && _str == r._str && _wstr == r._wstr; } }; void test_serializer() { mydata data; data._c = 'X'; data._w = L'q'; data._b = false; data._s = 12; data._l = 75543; data._64 = 0xabcdef0123; data._d = 12.47; data._str = "hello"; data._wstr = L"world"; std::stringstream ssm; serializer s(ssm); data.serialize(s); mydata data2; deserializer ds(ssm); data2.deserialize(ds); assert(data == data2); } 注意到了吗?实现一个对象的序列化/反序列化操作需要: 1. 从接口serializable中继承 2. 实现相应的接口。对于序列化,按照顺序依次把成员写入流;对于反序列化,则要严格按照相反的顺序依次读入 3. 如果某个成员不是基本数据类型,有两种选择:a. 实现该成员所属类型的序列化/反序列化,然后直接调用之;b. 直接序列化该成员的子成员。这样做会严重破坏对象的完整性,所以非常不推荐使用。 实现了对象的序列话之后,使用起来就特别容易了。在我们的测试代码中,我们把对象序列化为一个字串,然后反序列化之到另一个对象。我们最后得到两个状态完全一致的对象。如果我们把该字串通过socket传到远方,实际上就是对象的远程传递了。所有流行的远程对象调用都是用类似的基础机制达成其目的。
相关文章推荐
- C++基本功之 对象序列化
- C++对象序列化方案介绍
- c++ java中关于protobuf反序列化对象实体和实体处理(函数)关系(二)
- C++对象序列化简介与选型
- C++对象序列化
- C++实现对象序列化和反虚拟化(读写二进制文件)操作
- C++对象序列化方案对比
- 在C++中如何使用msgpack进行对象的序列化
- 使用C++进行对象序列化
- C/C++对象的序列化
- java与c++中的对象序列化分析
- C++对象的JSON序列化与反序列化探索
- C++ / QT 对象序列化(Object Serialization)的实现
- 使用 acl 库针对 C++ 对象进行序列化及反序列编程
- c++ java中关于protobuf反序列化对象实体和实体处理(函数)关系 (一)
- C++对象的JSON序列化与反序列化探索
- C++对象序列化简介与选型
- C++对象的JSON序列化与反序列化探索
- c++对象的序列化与反序列化的解决方案----flatbuffers的使用
- C++对象序列化