通过解除引用的基类指针提升序列化

时间:2013-07-15 13:36:58

标签: c++ serialization boost boost-serialization

我对boost序列化有点问题。有许多示例显示如何通过简单地使用BOOST_CLASS_EXPORT和BOOST_CLASS_EXPORT_IMPLEMENT来序列化通过基类指针的派生类指针。这工作正常,完全没有问题。

但是,我不想序列化一个指针,因为另一侧的反序列化应该再次在指针上,然后,boost会创建一个序列化对象的新实例。

我可以序列化一个解除引用的指针,然后在没有问题的情况下在现有对象实例上再次反序列化,并且不会创建新的实例。但是,当解除引用的指针位于基类之上时,在对指针进行序列化时,派生类不会按预期序列化。

工作示例:

Class A;
Class B : public A;

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

不工作的例子:

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

我可以通过对派生类进行简单的序列化来实现它,如:

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

但是我在结构中的所有引用都是基类类型。此外,我无法序列化指针,因为我不需要在反序列化时实例化新的objetcs,只在现有实例上“覆盖”内容。

我试图序列化指针并尝试对现有实例进行反序列化,但这不能正常工作。当我说对现有实例进行反序列化时,我的意思是:

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

正如我所说,反序列化时我不需要新的baseClass实例。那么,有没有办法让这个工作?

2 个答案:

答案 0 :(得分:1)

我遇到了同样的问题!所以我查看了boost的文档,它提供了解决问题的方法,我可以定义一个D类来管理派生对象,并使用ar.register_type来差异abc类,就像这样:

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

保存b时应保存哪种对象?加载b时应该创建哪种对象?它应该是类derived_one,derived_two还是base?

的对象

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

如果基类是多态的,则序列化最派生类型的对象(在本例中为derived_one或derived_two)。将序列化哪种类型的对象的问题(几乎)由库自动处理。

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

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

答案 1 :(得分:1)

我想我明白了这个问题。当你这样做

ar >> *DerivedClass;

您正在将参考传递给operator<<。现在,通过对基类的引用访问的对象没有正确序列化,正如我从Robert Ramey在Boost-users邮件列表中对this question的回答中收集的那样。虽然答案已经有好几年了,但我认为它仍然适用,因为如果你想到它,一个写的serialize方法不是虚拟的(它们是模板,所以它们是cannot be virtual)。 / p>

因此,库必须使用特殊的东西来处理指针,但它并没有使用引用。我发现一个丑陋的解决方案是添加一对(虚拟)序列化函数,如下所示:

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

其中iarchiveoarchive应替换为您想要的存档。这真的很糟糕,因为除了必须编写两个额外的函数之外,你必须明确地为所需的所有存档类型重载它们。不幸的是,我不知道更好的解决方案。