【问题标题】:Boost serialization, loading an archived class by base type gives wrong data提升序列化,按基类型加载归档类会给出错误数据
【发布时间】:2018-07-17 20:25:56
【问题描述】:

在将 Boost 的序列化库实现到项目之前,我编写了一个示例程序来找出它的序列化库,但是,我得到了一些无法解释的行为。


在我的示例中,我有两个类:一个通用的 BaseClass 和一个专用的 DerivedClass(类似于我计划使用 Boost 的目的)。 BaseClass 只有一个成员,一个名为name 的字符串,默认为“BaseClass”。 DerivedClass 公开继承 BaseClass,将 name 设置为其他内容并拥有自己的唯一成员 data

在主程序中,我创建了一个 DerivedClass 并将 data 设置为“特别酷的东西”,并创建一个 BaseClassname “常规的东西”。我将这两个都写入带有boost::archive::text_oarchive 的文件,然后将第一个对象DerivedClass 读回两次(两次都重新创建std::ifstream)。第一次读回来,我把它放到BaseClass*。调用BaseClass::printData()(打印std::typeidname 的虚拟方法)会打印以下内容:

 --- Storage done, now loading the first object as BaseClass --- 
9BaseClass: 0

接下来,当我将其加载为 DerivedClass* 并调用 DerivedClass::printData()(已从 BaseClass 覆盖以在输出中包含成员 data)时,会正确打印:

 --- Storage done, now loading the first object as DerivedClass --- 
12DerivedClass: DerivedClass AND special cool stuff

查看我正在写入的文件,我看到了:

22 serialization::archive 15 0 1 0
0 1 0
1 12 DerivedClass 18 special cool stuff 1
2 13 regular stuff

当我在原始的、预序列化的DerivedClass 上调用BaseClass::printData() 时,我得到了这个:

9BaseClass: DerivedClass

显然,DerivedClass 被正确存储。将其加载为BaseClass 以检查name 的事情搞砸了。我想不出它为什么会给我一个包含0std::string

我刚刚开始学习如何使用这个库,我在网上找到的大多数类似问题和文档都没有效果(即,使用 BOOST_EXPORT_CLASSBOOST_CLASS_TYPE_INFO,尽管它很可能是我用错了)。


这是我的代码:

main.cpp

#include <iostream>
#include <fstream>
#include <string>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/nvp.hpp>

#include "baseclass.h"
#include "derivedclass.h"

int main() {
    BaseClass*  testBase = new BaseClass("regular stuff");
    DerivedClass* testDerivate = new DerivedClass("special cool stuff");

    testDerivate->BaseClass::printData();

    std::cout << std::endl << " --- " << "Storing objects in the file 'output'..." << " --- " << std::endl;

    std::ofstream output("storage");

    {
        boost::archive::text_oarchive boost_out(output);
        boost_out << (testDerivate);
        testDerivate->printData();
        boost_out << (testBase);
        testBase->printData();
    }

    std::cout << std::endl << " --- " << "Storage done, now loading the first object as BaseClass" << " --- " << std::endl;

    {
        std::ifstream input("storage");
        BaseClass*  base;
        boost::archive::text_iarchive boost_in(input);
        boost_in >> (base);
        base->printData();
        input.close();
    }

    std::cout << std::endl << " --- " << "Storage done, now loading the first object as DerivedClass" << " --- " << std::endl;

    {
        std::ifstream input("storage");
        DerivedClass* derive;
        boost::archive::text_iarchive boost_in(input);
        boost_in >> (derive);
        derive->printData();
        input.close();
    }

    return 0;
}

基类.h

#pragma once

#include <string>
#include <iostream>
#include <typeinfo>

#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>

class BaseClass
{
public:
    BaseClass() {
        name = "BaseClass";
    }

    BaseClass(std::string custom) {
        name = custom;
    }

    virtual ~BaseClass() {}

    virtual void printData() {
        std::cout << typeid(*this).name() << ": " << name << std::endl;   
    }

protected:
    std::string name;

private:    
    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & (name);
    }
};

派生类.hpp

#pragma once

#include <string>
#include <iostream>
#include <typeinfo>

#include <boost/serialization/base_object.hpp>
#include <boost/serialization/access.hpp>

#include "baseclass.h"

class DerivedClass :  public BaseClass
{
public:
    DerivedClass() : BaseClass("DerivedClass") {}
    DerivedClass(std::string custom) : BaseClass("DerivedClass") {
        data = custom;
    }

    virtual ~DerivedClass() {}

    void printData() override {
        std::cout << typeid(*this).name() << ": " << name << " AND " << data << std::endl;
    }

protected:
    std::string data;

private:
    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & (boost::serialization::base_object<BaseClass>(*this));
        ar & (data);
    }
};

对不起,如果这有点长,我想尽可能描述。我对使用 Boost 很陌生,而且我对 C++ 的经验并不丰富,所以即使你的代码中只有一些通用的 cmets,我也会很感激。

【问题讨论】:

    标签: c++ serialization boost


    【解决方案1】:

    您加载的类型与序列化不同。

    所以,虽然可以说:

    Base* b = new Derived();
    boost_out << b;
    

    然后反序列化:

    Base* b = nullptr;
    boost_in >> b;
    

    不能序列化 Derived* 并将其反序列化为 Base*,反之亦然。

    因此,如果您知道接收代码必须支持所有派生类,请将其明确化并序列化 Base*

    导出类型

    为了让反序列化端知道在反序列化多态基指针时可能遇到的派生类型集是什么,请导出类型。

    演示

    Live On Wandbox

    • main.cpp

      #include <iostream>
      #include <fstream>
      #include <string>
      
      #include <boost/archive/text_iarchive.hpp>
      #include <boost/archive/text_oarchive.hpp>
      #include <boost/serialization/nvp.hpp>
      //#include <boost/serialization/export.hpp>
      
      #include "baseclass.h"
      #include "derivedclass.h"
      
      int main() {
          BaseClass* testBase = new BaseClass("regular stuff");
          BaseClass* testDerived = new DerivedClass("special cool stuff");
      
          std::cout << std::endl << " --- " << "Storing objects in the file 'output'..." << " --- " << std::endl;
      
          {
              std::ofstream output("storage");
              boost::archive::text_oarchive boost_out(output);
              boost_out << testBase << testDerived;
          }
      
          std::cout << std::endl << " --- " << "Storage done, now loading the first object as BaseClass" << " --- " << std::endl;
      
          {
              std::ifstream input("storage");
              BaseClass* b1;
              BaseClass* b2;
              boost::archive::text_iarchive boost_in(input);
              boost_in >> b1 >> b2;
      
              std::cout << "b1: "; b1->printData();
              std::cout << "b2: "; b2->printData();
          }
      }
      
    • 基类.h

      #pragma once
      
      #include <string>
      #include <iostream>
      #include <typeinfo>
      
      #include <boost/serialization/access.hpp>
      #include <boost/serialization/nvp.hpp>
      #include <boost/serialization/export.hpp>
      
      class BaseClass
      {
      public:
          BaseClass() {
              name = "BaseClass";
          }
      
          BaseClass(std::string custom) {
              name = custom;
          }
      
          virtual ~BaseClass() {}
      
          virtual void printData() {
              std::cout << typeid(*this).name() << ": " << name << std::endl;   
          }
      
      protected:
          std::string name;
      
      private:    
          friend class boost::serialization::access;
      
          template<class Archive>
          void serialize(Archive & ar, unsigned) {
              ar & (name);
          }
      };
      
      BOOST_CLASS_EXPORT_KEY2(BaseClass, "BaseClass");
      
    • 基类.cpp

      #include "baseclass.h"
      #include <boost/archive/text_oarchive.hpp>
      #include <boost/archive/text_iarchive.hpp>
      BOOST_CLASS_EXPORT_IMPLEMENT(BaseClass)
      
    • 派生类.h

      #pragma once
      
      #include <string>
      #include <iostream>
      #include <typeinfo>
      
      #include <boost/serialization/base_object.hpp>
      #include <boost/serialization/access.hpp>
      #include <boost/serialization/export.hpp>
      
      #include "baseclass.h"
      
      class DerivedClass :  public BaseClass
      {
      public:
          DerivedClass() : BaseClass("DerivedClass") {}
          DerivedClass(std::string custom) : BaseClass("DerivedClass") {
              data = custom;
          }
      
          virtual ~DerivedClass() {}
      
          void printData() override {
              std::cout << typeid(*this).name() << ": " << name << " AND " << data << std::endl;
          }
      
      protected:
          std::string data;
      
      private:
          friend class boost::serialization::access;
      
          template<class Archive>
          void serialize(Archive & ar, unsigned) {
              ar & (boost::serialization::base_object<BaseClass>(*this));
              ar & (data);
          }
      };
      
      BOOST_CLASS_EXPORT_KEY2(DerivedClass, "DerivedClass");
      
    • 派生类.cpp

      #include "derivedclass.h"
      #include <boost/archive/text_oarchive.hpp>
      #include <boost/archive/text_iarchive.hpp>
      BOOST_CLASS_EXPORT_IMPLEMENT(DerivedClass)
      

    输出:

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-12-13
    • 1970-01-01
    • 2017-07-03
    • 1970-01-01
    相关资源
    最近更新 更多