与序列多态类型常见的混乱 [英] Common confusions with serializing polymorphic types
问题描述
我见过很多问题,教程和文档涉及序列化派生类的,我一直没能达到的几个问题,包括达成共识(并在以下code所示):
-
的boost ::系列化:: base_object
VSBOOST_SERIALIZATION_BASE_OBJECT_NVP
-
归档和放大器; MDATA;
VS归档和放大器; BOOST_SERIALIZATION_NVP(MDATA);
-
BOOST_SERIALIZATION_ASSUME_ABSTRACT的用处(AbstractPoint);
- 要求
连载()
在分级中的类,这并不需要序列化任何东西。
code:
的#include<升压/存档/ text_oarchive.hpp>
#包括LT&;升压/存档/ text_iarchive.hpp>
#包括LT&;升压/系列化/ shared_ptr.hpp>
#包括LT&;升压/系列化/ base_object.hpp>#包括LT&;&的fstream GT;类AbstractPoint
{
上市:
虚拟〜AbstractPoint(){}
虚拟无效的doSomething()= 0; //即使是一个抽象类,我们仍然需要这种
模板<类TArchive>
无效连载(TArchive&安培;归档,const的无符号整型版)
{
// 没做什么
}
};//这似乎并不做任何事情
// BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);Point类:公共AbstractPoint
{
上市:
点()=默认值;
点(const的双倍数据):MDATA(数据){} 无效DoSomething的(){} 模板<类TArchive>
无效连载(TArchive&安培;归档,const的无符号整型版)
{
//这两个看似相同的。如果没有他们中的一个,未注册的投无效
归档和放大器;提高::系列化:: base_object< AbstractPoint>(*此);
//归档和放大器; BOOST_SERIALIZATION_BASE_OBJECT_NVP(AbstractPoint); //这两个看似等价
归档和放大器; MDATA;
//归档和放大器; BOOST_SERIALIZATION_NVP(MDATA);
} 双MDATA;
};诠释的main()
{
的std :: shared_ptr的< AbstractPoint>点(新点(7.4)); 的std ::的ofstream的OutputStream(的test.txt);
提高::档案:: text_oarchive的outputArchive(OutputStream中);
outputArchive.register_type<点和GT;();
outputArchive<<点;
outputStream.close(); 的std :: shared_ptr的< AbstractPoint> pointRead;
性病:: ifstream的InputStream的(的test.txt);
提高::档案:: text_iarchive inputArchive(InputStream的);
inputArchive.register_type<点和GT;();
inputArchive>> pointRead; 的std :: shared_ptr的<点和GT; castedPoint =的std :: dynamic_pointer_cast<点和GT;(pointRead);
性病::法院LT&;< castedPoint->&MDATA LT;<的std :: ENDL;
返回0;
}
另一个主要问题就是在一个真实的环境中注册类(当有链接等),但似乎值得一个单独的问题。
这将是巨大的,有这样的东西在文档中的金标准的例子,但在至少在计算器上:)
-
的boost ::系列化:: base_object
VSBOOST_SERIALIZATION_BASE_OBJECT_NVP
该NVP包装只会被要求有元素命名,如XML存档。
除非你使用它, base_object<方式>
是更清洁和更简单
-
归档和放大器; MDATA;
VS归档和放大器; BOOST_SERIALIZATION_NVP(MDATA);
同上
-
BOOST_SERIALIZATION_ASSUME_ABSTRACT的用处(AbstractPoint);
我以为这只会是一个最优化 - SUP pressing注册类型与每个存档类型的信息,因为你告诉框架它永远不会被反序列化该类型的实例
- 要求
连载()
在分级中的类,这并不需要序列化任何东西。
您不需要它,除非你需要大约多态基有类型的信息。当你需要的?当你需要为基本类型的反序列化的指针。
因此,如果你有
结构A {虚拟〜A(); };
结构A:A {};结构C:B {};
结构D:B {};`
在将的需要 A
序列化(但不是 B
)如果你( DE)连载 A *
。您需要序列化 B
如果你(反)序列化 B *
。
同样,如果你的类型是不是多态(虚拟),或者你不使用它是这样,你不需要任何基础的序列化(例如,如果你(反)序列化 C
或 D
直接)。
最后,如果你有结构A {};结构A:A {};
没有必要向大家介绍的基本类型升压序列化可言,(你可能只是做系列化从 B在
)。
更新响应您的样品:
- case1.cpp 看起来不错
-
case2.cpp 需要即可调用基序列当然;不一定使用base_object因为你需要多态序列化:
模板<类TArchive>无效连载(TArchive&安培;存档,无符号){
归档和放大器;提高::系列化:: base_object< AbstractPoint>(*此)
&安培; MDATA;
// 要么:
归档和放大器;的static_cast< AbstractPoint&安培;>(*此)
&安培; MDATA;
//甚至只是:
归档和放大器; mParentData
&安培; MDATA;
} -
case3.cpp :的确,它酷似情形1,但与动态分配和目标跟踪
-
case4.cpp :酷似情形1,但与动态分配和对象跟踪; NB!它需要为基本明确序列化!
模板<类TArchive>无效连载(TArchive&安培;存档,无符号){
归档和放大器;提高::系列化:: base_object< AbstractPoint>(*此)
&安培; MDATA;
} -
case5.cpp :是的,但它是更典型的使用
CLASS_EXPORT *
从升压/系列化/ export.hpp
宏
Bitrot保险:
块引用>I have seen many questions, tutorials, and documentation involving serializing derived classes, and I haven't been able to reach a consensus on several issues, including (and illustrated in the following code):
boost::serialization::base_object
vsBOOST_SERIALIZATION_BASE_OBJECT_NVP
archive & mData;
vsarchive & BOOST_SERIALIZATION_NVP(mData);
- The usefulness of
BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
- Requiring
serialize()
for a class in the hierarchy that doesn't need to serialize anything.Code:
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/shared_ptr.hpp> #include <boost/serialization/base_object.hpp> #include <fstream> class AbstractPoint { public: virtual ~AbstractPoint(){} virtual void DoSomething() = 0; // Even though the class is abstract, we still need this template<class TArchive> void serialize(TArchive& archive, const unsigned int version) { // do nothing } }; // This doesn't seem to do anything //BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint); class Point : public AbstractPoint { public: Point() = default; Point(const double data) : mData(data) {} void DoSomething(){} template<class TArchive> void serialize(TArchive& archive, const unsigned int version) { // These two seem equivalent. Without one of them, unregistered void cast archive & boost::serialization::base_object<AbstractPoint>(*this); //archive & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AbstractPoint); // These two seem equivalent archive & mData; //archive & BOOST_SERIALIZATION_NVP(mData); } double mData; }; int main() { std::shared_ptr<AbstractPoint> point(new Point(7.4)); std::ofstream outputStream("test.txt"); boost::archive::text_oarchive outputArchive(outputStream); outputArchive.register_type<Point>(); outputArchive << point; outputStream.close(); std::shared_ptr<AbstractPoint> pointRead; std::ifstream inputStream("test.txt"); boost::archive::text_iarchive inputArchive(inputStream); inputArchive.register_type<Point>(); inputArchive >> pointRead; std::shared_ptr<Point> castedPoint = std::dynamic_pointer_cast<Point>(pointRead); std::cout << castedPoint->mData << std::endl; return 0; }
The other major issue is where to register classes in a "real" environment (when there is linking, etc.), but that seems worth a separate question.
It would be great to have a "gold standard" example of these kinds of things in the documentation, but at the least on StackOverflow :)
解决方案
boost::serialization::base_object
vsBOOST_SERIALIZATION_BASE_OBJECT_NVP
The NVP wrapper is only ever required for archives that have element naming, like XML.
Unless you use it,
base_object<>
is cleaner and simpler.
archive & mData;
vsarchive & BOOST_SERIALIZATION_NVP(mData);
Ditto
- The usefulness of
BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
I assume it will merely be an optimization - suppressing registered type information with each archive type, since you told the framework it will never be de-serializing instances of the type
- Requiring
serialize()
for a class in the hierarchy that doesn't need to serialize anything.You don't need it, unless you need the type information about a polymorphic base there. When do you need that? When you need to de-serialize pointers of the base type.
Hence, if you have
struct A{ virtual ~A(); }; struct B:A{}; struct C:B{}; struct D:B{};`
you will need serialization for
A
(but notB
) if you (de)serializeA*
. You will need serialization forB
if you (de)serializeB*
.Similarly, if your type is not polymorphic (virtual) or you don't use it as such, you don't need any base serialization (e.g. if you (de)serialize
C
orD
directly).Finally, if you have
struct A{}; struct B:A{};
there is no need to tell Boost Serialization about the base type at all, (you could just do the serialization from withinB
).Update in response to your samples:
- case1.cpp looks ok
case2.cpp needs to call base serialization, of course; not necessarily using base_object because you require polymorphic serialization:
template<class TArchive> void serialize(TArchive& archive, unsigned) { archive & boost::serialization::base_object<AbstractPoint>(*this) & mData; // OR: archive & static_cast<AbstractPoint&>(*this) & mData; // OR even just: archive & mParentData & mData; }
case3.cpp: indeed, it's exactly like case1, but with dynamic allocation and object tracking
case4.cpp: is exactly like case1, but with dynamic allocation and object tracking; NB!! it requires explicitly serializing for the base!
template<class TArchive> void serialize(TArchive& archive, unsigned) { archive & boost::serialization::base_object<AbstractPoint>(*this) & mData; }
case5.cpp: yes, but it's more typical to use the
CLASS_EXPORT*
macros fromboost/serialization/export.hpp
Bitrot insurance:
这篇关于与序列多态类型常见的混乱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!