烟台做网站多钱,wordpress字体更换,给网站做数据分析,wordpress 用户关注开发需要使用到序列化场景的需求
在写代码的过程中#xff0c;经常会需要把代码层面的对象数据保存到文件#xff0c;而这些数据会以各种格式存储#xff0e;例如#xff1a;json#xff0c;xml#xff0c;二进制等等#xff0e;二进制#xff0c;相比json#xff0c;xml…需要使用到序列化场景的需求
在写代码的过程中经常会需要把代码层面的对象数据保存到文件而这些数据会以各种格式存储例如jsonxml二进制等等二进制相比jsonxml格式直接把字节copy到硬盘所以这实现起来相对容易
例子 下面是一个简单的三维向量结构体如何把它序列化到文件呢
struct Vec3 {float x;float y;float z;
}
Vec3 v;
v.x 1.0f;
v.y 2.0f;
v.z 3.0f;
os.write((const char *)v, sizeof(Vec3));
上述是序列化Vec3对象数据到文件的代码非常直接它的内存布局是3个浮点型变量紧凑排列要把它存储到硬盘只要从头到尾按字节拷贝即可但是在实际开发中要序列化的对象不可能全部都是内存紧凑排列的例如STL容器
std::vectorVec3 vec;
如果将容器变量从头到尾拷贝到文件必然会出现错误因为容器内部通过一个指针来访问存储的对象而直接拷贝这个容器只会把指针拷贝指针指向的数据却丢失了但是容器提供了一个可以直接访问指针指向数据的接口可以通过这个接口得到数据然后直接拷贝
os.write((const char *)vec, vec.size() * sizeof(Vec3)); // 错误, 仅拷贝指针
os.write((const char *)vec.data(), vec.size() * sizeof(Vec3)); // 正确, 数据被完全拷贝
通过这个方法就可以得到正确的拷贝结果了通常好的做法是将序列化和反序列化封装成接口以便于使用如何封装接口就是这篇文章的主题从上述两个例子可以发现对于单体对象和数组对象编写的代码是不一样的单体对象直接拷贝数组对象需要通过 .data() 取得数据地址再进行拷贝而考虑到还有嵌套数组像 std::vectorstd::vectorVec3对于嵌套数组序列化的代码可能如下
std::vectorstd::vectorVec3 vec2;
for (auto vec: vec2)
{os.write((const char *)vec.data(), vec.size() * sizeof(Vec3));
}
可以发现对嵌套数组对象的序列化代码跟上述2种对象又不一样考虑到还有N层嵌套的数组对象此外在C中有一个 可平凡复制的概念 通俗的说就是可以直接按字节拷贝的结构称之为 可平凡复制 上述的 Vec3 则是一个可平凡复制结构而STL容器则不是可平凡复制结构除此之外还有更多不可平凡复制且非容器的结构故此如果要封装接口除了区分单体对象和数组对象还要区分可平凡复制和不可平凡复制
void Serialize(std::ostream os, const Type val); // 序列化
void Deserialize(std::istream is, Type val); // 反序列化
// POD
template class T, typename std::enable_if_tstd::is_trivially_copyable_vT, int N 0
void Serialize(std::ostream os, const T val)
{os.write((const char *)val, sizeof(T));
}// 容器
template class T, typename std::enable_if_tstd::is_same_vtypename T::iterator, decltype(std::declvalT().begin()) std::is_same_vtypename T::iterator, decltype(std::declvalT().end()), int N 0void Serialize(std::ostream os, const T val)
{unsigned int size val.size();os.write((const char *)size, sizeof(size));for (auto v : val) { Serialize(os, v); }
}// POD
template class T, typename std::enable_if_tstd::is_trivially_copyable_vT, int N 0
void Deserialize(std::istream is, T val)
{is.read((char *)val, sizeof(T));
}// 容器
template class T, typename std::enable_if_tstd::is_same_vtypename T::iterator, decltype(std::declvalT().begin()) std::is_same_vtypename T::iterator, decltype(std::declvalT().end()), int N 0void Deserialize(std::istream is, T val)
{unsigned int size 0;is.read((char *)size, sizeof(unsigned int));val.resize(size);for (auto v : val) { Deserialize(is, v); }
}
以上实现可序列化任意 可平凡拷贝 结构并且也可序列化任意嵌套层数的STL风格数组而对于 不可平凡复制 结构只需要针对该结构重载即可借助C强大的类型推导机制和SFINEA机制可保证类型安全又具备可扩展性