在取消引用的基类指针上增强序列化
Boost serialization over dereferenced base class pointer
我对boost序列化有点问题。有许多示例显示了如何通过基类指针简单地使用BOOST_class_EXPORT和BOOST_LASS_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
但是我在结构中的所有引用都是基类类型的。此外,我无法序列化指针,因为在反序列化时,我不需要实例化新的对象,只需要在现有实例上"覆盖"内容。
我曾尝试序列化指针,并尝试在现有实例上进行反序列化,但这无法正常工作。当我说对现有实例进行反序列化时,我的意思是:
A* baseClass = new B();
// baseClass is used in the program and in a given moment, its contents must be overwrite, so:
ar >> *baseClass;
正如我所说,在反序列化时,我不需要基类的新实例。那么,有什么办法让它发挥作用吗?
我和你遇到了同样的问题所以我查看了boost的文档,它提供了一种解决问题的方法,我可以定义一个类D来管理派生对象,并使用ar.register_type
来区分a b c类,就像这样:
class base {
...
};
class derived_one : public base {
...
};
class derived_two : public base {
...
};
main(){
...
base *b;
...
ar & b;
}
保存b时,应该保存什么类型的对象?加载b时,应该创建什么类型的对象?它应该是derived_one、derived_two类的对象,还是可能是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
我想我理解这个问题。当你做
ar >> *DerivedClass;
您正在向operator<<
传递引用。现在,通过引用基类访问的对象没有正确序列化,正如我在Boost用户邮件列表中从Robert Ramey对这个问题的回答中收集到的那样。尽管答案已经有好几年的历史了,但我认为它仍然成立,因为,如果你仔细想想,一个人写的serialize
方法不是虚拟的(它们是模板,所以不可能是虚拟的)。
因此,库必须做一些特殊的事情来处理指针,但它不是用引用来做的。我发现的一个丑陋的解决方案是添加一对(虚拟)序列化函数,如下所示:
virtual myser(iarchive &ia) {ia >> *this;}
virtual myser(oarchive &oa) {oa << *this;}
其中iarchive
和oarchive
应替换为所需的存档。这真的很糟糕,因为除了必须编写两个额外的函数外,还必须明确地为所有需要的归档类型重载它们。不幸的是,我不知道有什么更好的解决方案。
- 增强基于 XML class_id的反序列化
- 增强序列化,按基类型加载存档类会产生错误的数据
- 增强多重继承的序列化
- 来自抽象基础的次要化因增强序列化而失败
- 如何更改或删除标签,以增强序列化
- 崩溃:分段故障:增强序列化加载 - 用null调用构造函数
- 在同一程序中使用谷物并增强序列化
- 使用指针增强序列化
- 如何增强序列化STL容器
- 增强序列化多态性类
- 增强动态阵列的序列化
- 增强多边形序列化:环
- 多个库并增强序列化
- 增强序列化-启用对象跟踪
- 在取消引用的基类指针上增强序列化
- c++增强序列化如何防止不正确的文件崩溃
- 跟踪何时为增强序列化添加数据成员
- 链接到增强序列化时出错
- 处理可能的空指针的类的增强序列化
- 增强序列化-在反序列化损坏数据时不再出现archive_exception