【问题标题】:Problem in serialization of derived class pointer into a vector with boost::serialization使用 boost::serialization 将派生类指针序列化为向量的问题
【发布时间】:2019-11-20 21:38:24
【问题描述】:

我有一段代码在其中定义了以下类:Base、Derived、Contaienr。

Derived 显然继承了Base,而Container 包含Base 共享指针的向量,可以是Base 和Derived 的指针。

我想序列化Container,因此向量的元素分别序列化为Base 和Derived,但它似乎不起作用。

这是我的测试代码:

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/export.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>

/// Base class ///
class Base {
public:

  using Ptr = std::shared_ptr<Base>;

public:

  void setDouble(double d) {
    m_d = d;
  }

  void setInteger(int c) {
    m_c = c;
  }

  double getDouble() const {
    return m_d;
  }

  int getInteger() const {
    return m_c;
  }

private:

  double m_d;
  int m_c;
};

/// Derived class from Base ///
class Derived : public Base {
public:

  using Ptr = std::shared_ptr<Derived>;

public:

  void setString(const std::string& s) {
    m_s = s;
  }

  const std::string& getString() const {
    return m_s;
  }

private:

  std::string m_s;
};

/// Container of base class pointers ///
class Container {
public:

  void addData(Base::Ptr data) {
    m_data.push_back(data);
  }

  const std::vector<Base::Ptr>& getDataVector() const {
    return m_data;
  }

private:

  std::vector<Base::Ptr> m_data;
};

BOOST_SERIALIZATION_SPLIT_FREE(Base)
BOOST_SERIALIZATION_SPLIT_FREE(Derived)
BOOST_SERIALIZATION_SPLIT_FREE(Container)
BOOST_CLASS_EXPORT_GUID(Derived, "Derived")

namespace boost {
namespace serialization {

/// Serialization of base class ///
template<class Archive>
void save(Archive& ar, const Base& m, unsigned int) {
  auto d = m.getDouble();
  auto i = m.getInteger();
  ar& make_nvp("doublevalue", d);
  ar& make_nvp("intvalue", i);
}

template<class Archive>
void load(Archive& ar, Base& m, unsigned int) {
  double d;
  int i;

  ar& make_nvp("doublevalue", d);
  ar& make_nvp("intvalue", i);

  m.setDouble(d);
  m.setInteger(i);
}

/// serialization of derived class ///
template<class Archive>
void save(Archive& ar, const Derived& m, unsigned int) {
  ar& make_nvp("base", base_object<const Base>(m));
  ar& make_nvp("stringvalue", m.getString());
}

template<class Archive>
void load(Archive& ar, Derived& m, unsigned int) {
  std::string s;

  ar& make_nvp("base", base_object<Base>(m));
  ar& make_nvp("stringvalue", s);

  m.setString(s);
}

/// serialization of container class ///
template<class Archive>
void save(Archive& ar, const Container& m, unsigned int) {
  ar& make_nvp("data", m.getDataVector());
}

template<class Archive>
void load(Archive& ar, Container& m, unsigned int) {
  std::vector<Base::Ptr> data;

  ar& make_nvp("data", data);

  for (const auto& it : data) {
    m.addData(it);
  }
}

}
} // namespace boost::serialization


int main(int argc, char *argv[]) {
  // Initialize container
  Container container;
  auto baseObj = std::make_shared<Base>();
  baseObj->setDouble(4.3);
  baseObj->setInteger(6);
  auto derivedObj = std::make_shared<Derived>();
  derivedObj->setDouble(1.1);
  derivedObj->setInteger(2);
  derivedObj->setString("string in derived");
  container.addData(baseObj);
  container.addData(derivedObj);

  // Print serialization of Base
  std::stringstream basess;
  boost::archive::xml_oarchive baseoa{basess};
  baseoa <<  boost::serialization::make_nvp("baseclass", baseObj);
  std::cout << basess.str() << std::endl;

  // Print serialization of Derived
  std::stringstream derivedss;
  boost::archive::xml_oarchive derivedoa{derivedss};
  derivedoa <<  boost::serialization::make_nvp("derivedclass", derivedObj);
  std::cout << derivedss.str() << std::endl;

  // Print serialization of Container
  std::stringstream containerss;
  boost::archive::xml_oarchive containeroa{containerss};
  containeroa <<  boost::serialization::make_nvp("containerclass", container);
  std::cout << containerss.str() << std::endl;
  return 0;
}

当我运行程序时,我打印baseObj 的序列化,这是Base 的共享指针:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="17">
<baseclass class_id="0" tracking_level="0" version="1">
    <px class_id="1" tracking_level="1" version="0" object_id="_0">
        <doublevalue>4.29999999999999982e+00</doublevalue>
        <intvalue>6</intvalue>
    </px>
</baseclass>

这似乎是正确的,因为我在基类中同时定义了 doublevalue 和 intvalue。

然后我打印derivedObj 的序列化,这是Derived 的共享指针:

<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="17">
<derivedclass class_id="0" tracking_level="0" version="1">
    <px class_id="1" tracking_level="1" version="0" object_id="_0">
        <base class_id="2" tracking_level="1" version="0" object_id="_1">
            <doublevalue>1.10000000000000009e+00</doublevalue>
            <intvalue>2</intvalue>
        </base>
        <stringvalue>string in derived</stringvalue>
    </px>
</derivedclass>

它似乎按预期工作,因为我有基类数据,还有派生类的stringvalue。

现在,如果我将两个指针都放入 Container 中的 std::vector&lt;std::shared_ptr&lt;Base&gt;&gt; 中,我期望正确地序列化 baseObj 和 derivedObj。相反,这是输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="17">
<containerclass class_id="0" tracking_level="0" version="0">
    <data class_id="1" tracking_level="0" version="0">
        <count>2</count>
        <item_version>1</item_version>
        <item class_id="2" tracking_level="0" version="1">
            <px class_id="3" tracking_level="1" version="0" object_id="_0">
                <doublevalue>4.29999999999999982e+00</doublevalue>
                <intvalue>6</intvalue>
            </px>
        </item>
        <item>
            <px class_id_reference="3" object_id="_1">
                <doublevalue>1.10000000000000009e+00</doublevalue>
                <intvalue>2</intvalue>
            </px>
        </item>
    </data>
</containerclass>

vector 的两个元素都被序列化为Base 指针。

我已尝试按照文档中的建议使用 BOOST_CLASS_EXPORT_GUID(Derived, "Derived") 宏,但它似乎不起作用。

我也尝试了this post 中提出的解决方案,通过注释BOOST_CLASS_EXPORT_GUID(Derived, "Derived") 并在Container 的序列化中使用register_type,但问题仍然存在:

/// serialization of container class ///
template<class Archive>
void save(Archive& ar, const Container& m, unsigned int) {
  ar.template register_type<Derived>();
  ar& make_nvp("data", m.getDataVector());
}

template<class Archive>
void load(Archive& ar, Container& m, unsigned int) {
  ar.template register_type<Derived>() ;
  std::vector<Base::Ptr> data;

  ar& make_nvp("data", data);

  for (const auto& it : data) {
    m.addData(it);
  }
}

如何正确序列化存储在Base 的共享指针向量中的Derived 类?

【问题讨论】:

  • 我想你可能会感兴趣:stackoverflow.com/a/30210929/4658169
  • 您需要保存矢量大小而不是每个对象数据 Derived + Base 然后在加载时获取并使用您加载的信息重建它们并插入到容器中

标签: c++ serialization boost boost-serialization


【解决方案1】:

部分问题可能是在派生类的情况下std::shared_ptr 的行为。所以你有必要用一个普通的指针替换std::shared_ptr。

struct A
{

};

struct B : public A
{

};

void fun(const std::shared_ptr<A>& base)
{
    std::cout << typeid(base).name() << std::endl;
}

int main(int argc, char *argv[]) {
    auto a=std::make_shared<A>();
    auto b=std::make_shared<B>();

  std::cout << typeid(a).name() << std::endl;
  std::cout << typeid(b).name() << std::endl;
  fun(a);
  fun(b);
}

这给了你,你可能会认为第二行和第四行是相等的:

class std::shared_ptr<struct A>
class std::shared_ptr<struct B>
class std::shared_ptr<struct A>
class std::shared_ptr<struct A>

第二个但不那么明显的一点是,您的基类应该至少包含一个virtual function。你可以使析构函数成为一个虚函数,只需包括:

virtual ~Base() {};

文档说:

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

在我用普通指针替换所有 shared_ptr 并添加虚拟析构函数后,结果符合预期,我获得了最后一部分的以下输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="17">
<containerclass class_id="0" tracking_level="0" version="0">
        <data class_id="1" tracking_level="0" version="0">
                <count>2</count>
                <item_version>0</item_version>
                <item class_id="2" tracking_level="1" version="0" object_id="_0">
                        <doublevalue>4.29999999999999982e+00</doublevalue>
                        <intvalue>6</intvalue>
                </item>
                <item class_id="3" class_name="Derived" tracking_level="1" version="0" object_id="_1">
                        <base object_id="_2">
                                <doublevalue>1.10000000000000009e+00</doublevalue>
                                <intvalue>2</intvalue>
                        </base>
                        <stringvalue>string in derived</stringvalue>
                </item>
        </data>
</containerclass>

【讨论】:

  • 在基类中添加虚拟析构函数解决了这个问题。我没有用原始指针更改共享指针,为了解决问题没有必要。感谢您的帮助。
  • @Jepessen:很好的提示。我真的不知道 boost-serialization 如何区分 shared_ptr 实际上包含一个 Derived 成员。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 2014-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-21
相关资源
最近更新 更多