2

I have a little problem with boost serialization. There are many examples that shows how to serialize a derived class pointer through the base class pointer by simply using BOOST_CLASS_EXPORT and BOOST_CLASS_EXPORT_IMPLEMENT. This is working fine and have no problems at all.

However, I do not want to serialize a pointer, as the deserialization in the other side should be again over a pointer and then, boost creates a new instance of the serialized object.

I can serialize a dereferenced pointer and then deserialize again over an existing object instance without problems, and no new instances are created. However, when the dereferenced pointer is over the base class, the derived class is not serialized as expected when serializing over pointers.

Working example:

Class A;
Class B : public A;

A* baseClass = new B();
ar << baseClass // works perfectly

Not working example:

Class A;
Class B : public A;
A* baseClass = new B();
ar << *baseClass; // only A is serialized

I can get it working by simple serializing over the derived class like:

B* derivedClass = new B();
ar << *derivedClass; // works fine

But all the references I have in my structures are of base class type. Also I cannot serialize the pointer as I do not need to instantiate new objetcs when deserializing, only "overwrite" the contents over an existing instance.

I have tried to serialize the pointer and trying to deserialize over an existing instance, but this does not work correctly. When I say deserialize over an existing instance, I mean:

A* baseClass = new B();
// baseClass is used in the program and in a given moment, its contents must be overwrite, so:
ar >> *baseClass;

As I said, I do not need a new instance of baseClass when deserializing. So, is there any way to get this working?

4

2 回答 2

2

我和你遇到了同样的问题!所以我查看了 boost 的文档,它给出了解决问题的方法,我可以定义一个类 D 来管理派生对象,并使用ar.register_typediff 来区分 abc 类,就像这样:</p>

 class base {
    ...
};
class derived_one : public base {
    ...
};
class derived_two : public base {
    ...
};
main(){
    ...
    base *b;
    ...
    ar & b; 
}

保存 b 时应该保存什么样的对象?加载 b 时应该创建什么样的对象?它应该是派生类、派生类二还是基类的对象?

事实证明,序列化的对象类型取决于基类(在本例中为基类)是否是多态的。如果 base 不是多态的,即没有虚函数,那么 base 类型的对象将被序列化。任何派生类中的信息都将丢失。如果这是所需要的(通常不是),则不需要其他努力。

如果基类是多态的,那么最派生类型的对象(在这种情况下是derived_one 或derived_two)将被序列化。要序列化哪种类型的对象的问题(几乎)由库自动处理。

系统第一次将每个类“注册”在档案中,该类的对象被序列化并为其分配一个序列号。下次该类的对象在同一存档中序列化时,此编号将写入存档中。因此,每个类在档案中都是唯一标识的。当档案被读回时,每个新的序列号都与正在读取的类重新关联。请注意,这意味着“注册”必须在保存和加载期间发生,以便加载时构建的类整数表与保存时构建的类整数表相同。事实上,整个序列化系统的关键是事物总是以相同的顺序保存和加载。这包括“注册”。

main(){
    derived_one d1;
    derived_two d2:
    ...
    ar & d1;
    ar & d2;
    // A side effect of serialization of objects d1 and d2 is that
    // the classes derived_one and derived_two become known to the archive.
    // So subsequent serialization of those classes by base pointer works
    // without any special considerations.
    base *b;
    ...
    ar & b; 
}

当 b 被读取时,它前面有一个唯一的(对存档的)类标识符,该标识符以前与类 derived_one 或 derived_two 相关。

如果派生类没有如上所述自动“注册”,则在调用序列化时将引发 unregistered_class 异常。

这可以通过显式注册派生类来解决。所有档案都派生自实现以下模板的基类:

template<class T>
register_type();

所以我们的问题也可以通过编写来解决:

main(){
    ...
    ar.template register_type<derived_one>();
    ar.template register_type<derived_two>();
    base *b;
    ...
    ar & b; 
}

请注意,如果序列化函数分为保存和加载,则这两个函数都必须包含注册。这是保持保存和相应加载同步所必需的。

您还可以使用:

#include <boost/serialization/export.hpp>
...
BOOST_CLASS_EXPORT_GUID(derived_one, "derived_one")
BOOST_CLASS_EXPORT_GUID(derived_two, "derived_two")

main(){
    ...
    base *b;
    ...
    ar & b; 

宏 BOOST_CLASS_EXPORT_GUID 将字符串文字与类相关联。在上面的例子中,我们使用了类名的字符串渲染。如果此类“导出”类的对象通过指针序列化并且未注册,则“导出”字符串包含在存档中。稍后读取存档时,字符串文字用于查找应由序列化库创建的类。这允许每个类与其字符串标识符一起位于单独的头文件中。无需维护可能被序列化的派生类的单独“预注册”。这种注册方法称为“密钥导出”。

也许对你有帮助!!有关详细信息,您可以看到: http: //www.boost.org/doc/libs/1_54_0/libs/serialization/doc/index.html

于 2013-07-15T13:55:04.180 回答
1

I think I understand the problem. When you do

ar >> *DerivedClass;

you are passing a reference to operator<<. Now objects accessed through references to a base class are not properly serialized, as I gather from Robert Ramey's answer to this question in the Boost-users mailing list. Although the answer is several years old, I think it still holds true, because, if you think of it, the serialize methods one writes are not virtual (they are templates, so they cannot be virtual).

So the library must be doing special stuff to handle pointers, but it is not doing it with references. An ugly solution I found is to add a pair of (virtual) serialize functions, like this:

virtual myser(iarchive &ia) {ia >> *this;}
virtual myser(oarchive &oa) {oa << *this;}

where iarchive and oarchive should be replaced with the archive you want. This sucks really, because apart from having to write two extra functions, you must explictly overload them for all the archive types you need. Unfortunately I do not know of a better solution.

于 2014-02-04T22:17:35.083 回答