Boost序列化引用成员抽象类 [英] Boost serialization of reference member abstract class

查看:249
本文介绍了Boost序列化引用成员抽象类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄明白如何序列化一个类,我把Boost放在一起。我会得到代码:

I'm trying to figure out how to serialize a class that I put together with Boost. I'll get right to the code:

#ifndef TEST_H_
#define TEST_H_

#include <iostream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>

class Parent
{
public:
        int test_val = 1234234;
        int p()
        {
                return 13294;
        }
        int get_test_val()
        {
                std::cout << test_val << std::endl;
                return test_val;
        }
        friend class boost::serialization::access;
        template<class Archive>
            void serialize(Archive &ar, const unsigned int /*version*/)
        {
                ar &test_val;
        }
};

class RefMem : public Parent
{
public: 
        RefMem()
        {
                test_val = 12342;
                std::cout << test_val << std::endl;
        }
};


class Test
{
public:
        friend class boost::serialization::access;
        int t_;
        Parent &parent_;
        Test(int t, Parent &&parent = RefMem());
        template<class Archive>
        void serialize(Archive &ar, const unsigned int file_version){
                ar &t_;
                ar &parent_;
        }
        //template<class
};


#endif





$ b b


#include "test.h"
#include <iostream>
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

Test :: Test(int t, Parent &&parent) : parent_(parent)
{
        std::cout << this->parent_.test_val << std::endl;
        t_ = t;
        parent_ = parent;
}

    int main()
    {
            Test test = Test(50);
            std::cout << "t_: " << test.t_ << std::endl;
            std::cout << "Test val: " << test.parent_.get_test_val() << std::endl;
            std::ostringstream oss;
            {
                    boost::archive::text_oarchive oa(oss);
                    oa << test;
            }

            Test cloned;
            std::istringstream iss(oss.str());
                {   
                    boost::archive::text_iarchive ia(iss);
                    ia >> cloned;
                }


            std::cout << "t_: " << cloned.t_ << std::endl;
            std::cout << "Test val: " << cloned.parent_.get_test_val() << std::endl;
    }

我基本上是在黑暗中拍摄。我是C ++的新手,我可以得到一个基本的例子来工作,但没有像这样,我序列化一个引用成员是一个抽象类的孩子,然后反序列化它。这段代码只是复制我想在另一个程序中做的。我有一些随机函数/变量只是为了测试。

I'm basically shooting in the dark. I'm new to C++ and I could get a basic example to work, but nothing like this where I serialize a reference member that is a child of an abstract class and then deserialize it. This code is just replicating what I'm trying to do in another program. I have a few random functions/variables just for testing.

编辑:我将如何让这段代码编译和工作正常?

How would I get this code to compile and work properly?

推荐答案

您对引用的所有权语义感到困惑。

You're confused about the ownership semantics of references.


  1. 引用 parent _ 只是指向 RefMem ¹ 。当你序列化时,编写这些(因为它们是左值引用,值本身已经被序列化)是容易的。

  1. The reference parent_ merely "points" to an instance of RefMem¹. When you serialize, it's "easy" to write these (because they're lvalue-references, the value itself will have been serialized).

然而,对于反序列化,事情并不那么简单,只是因为我们没有 MemRef 的实例指向。我们可以预期Boost序列化(以某种方式)动态实例化一个 MemRef 的空气,并默默作为参考点。

However for deserialization, things are not so simple, simply because we do-not have an instance of MemRef to "point" to. We could expect Boost Serialization to (somehow) dynamically instantiate a MemRef out of thin air and silently make the reference point to it. However, at best this will lead to memory leaks.

有关参考成员的另一件事。参考成员只能在构造函数的初始化器列表中初始化。

There's another thing about reference members specifically. Reference member can only be initialized in the constructor's initializer list.

由于Boost序列化序列化它不构造这些对象,

Because Boost Serialization serializes values it does not construct these objects, and the question is how the reference can even be initialized at all.

您当前的构造函数有一些相关问题:

Your current constructor has a number of related issues:

Test(int t, Parent && parent = RefMem()) : parent_(parent) {
    std::cout << __FUNCTION__ << ":" << this->parent_.test_val << "\n";
    t_      = t;
    parent_ = parent; // OOPS! TODO FIXME
}




  • 首先,构造函数禁用编译器 - 生成的默认构造函数,因此,确实, Test cloned; 甚至不能编译

  • 其次,默认参数 parent 是一个右值引用,一旦构造函数返回,它就变得悬空。您的计划有未定义的行为

  • 第三,

    • firstly, the constructor disables the compiler-generated default constructor, so that, indeed, the line Test cloned; couldn't even compile
    • secondly, the default argument for parent is a rvalue-reference and it becomes dangling as soon as the constructor returns. Your program has Undefined Behaviour
    • Thirdly the line

      parent_ = parent; // OOPS! TODO FIXME
      

      不会执行您认为的操作。它将 Parent 对象的值复制到 parent > parent _ 引用的对象。这很可能不可见,因为 parent _ parent 在这里是相同的对象,但甚至涉及对象切片什么是对象切片?)。

      doesn't do what you think it does. It copies the value of the Parent object from parent over the object referred to by parent_. This is likely not visible as parent_ and parent are the same object here, but there's even Object Slicing involved (What is object slicing?).






      什么?



      最好重新分组并打开文档引用的序列化


      包含引用成员的类通常需要非默认的
      构造函数作为引用在构建实例时设置。
      如果类有
      引用成员,上一节的示例稍微更复杂一些。 这会引发一个问题,即引用的对象
      的存储方式和位置,以及如何创建它们。
      还有关于引用多态基类的
      问题。基本上,这些是
      与指针相同的问题。这不奇怪,因为
      引用真的是一种特殊的指针。

      Classes that contain reference members will generally require non-default constructors as references can only be set when an instance is constructed. The example of the previous section is slightly more complex if the class has reference members. This raises the question of how and where the objects being referred to are stored and how are they created. Also there is the question about references to polymorphic base classes. Basically, these are the same questions that arise regarding pointers. This is no surprise as references are really a special kind of pointer.

      我们通过序列化引用来解决这些问题,好像他们是
      指针。


      文档建议 load_construct_data / save_construct_data 以减轻 Test

      The documentation does go on to suggest load_construct_data/save_construct_data to alleviate the non-default-constructibility of Test.

      请注意,他们将引用成员作为指针处理的建议似乎很好,但有意义,如果实际指向的对象是还通过同一个归档中的指针 进行序列化。在这种情况下,对象跟踪会发现

      Note that their suggestion to handle the reference member as a pointer seems nice, but it only makes sense if the actual pointed-to object is also serialized through a pointer in the same archive. In such case Object Tracking will spot the aliasing pointer and avoid creating a duplicate instance.

      如果没有,你仍然会有你的内存泄漏,以及可能破坏的程序状态。

      If not, you'll still have your memory leak, and possibly broken program state.

      。注意,我们泄漏动态分配的对象。我不喜欢这种风格,因为它本质上是将引用看作是一个指针。

      Here's a demo of essentially the technique outlined above. Note that we're leaking the dynamically allocated objects. I don't like this style because it's essentially treating the reference as if it were a pointer.

      如果这是我们需要的考虑使用指针(见下文)

      If that's what we want, we should consider using pointers (see below)

      Live on Coliru

      #ifndef TEST_H_
      #define TEST_H_
      
      #include <iostream>
      #include <boost/serialization/serialization.hpp>
      #include <boost/archive/binary_oarchive.hpp>
      #include <boost/archive/binary_iarchive.hpp>
      
      class Parent {
        public:
          int test_val = 1234234;
      
          int p() { return 13294; }
      
          int get_test_val() {
              std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
              return test_val;
          }
      
          template <class Archive> void serialize(Archive &ar, unsigned) {
              ar & test_val; 
          }
      };
      
      class RefMem : public Parent {
        public:
          RefMem() {
              test_val = 12342;
              std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
          }
      };
      
      class Test {
        public:
          friend class boost::serialization::access;
          int t_;
          Parent &parent_;
      
          Test(int t, Parent& parent) : parent_(parent) {
              std::cout << __PRETTY_FUNCTION__ << ":" << this->parent_.test_val << "\n";
              t_      = t;
          }
      
          template <class Archive> void serialize(Archive &ar, const unsigned int file_version) {
              ar &t_;
              //ar &parent_; // how would this behave? We don't own it... Use pointers
          }
          // template<class
      };
      
      namespace boost { namespace serialization {
          template<class Archive>
              inline void save_construct_data(Archive & ar, const Test * t, const unsigned int file_version) {
                  // save data required to construct instance
                  ar << t->t_;
                  // serialize reference to Parent as a pointer
                  Parent* pparent = &t->parent_;
                  ar << pparent;
              }
      
          template<class Archive>
              inline void load_construct_data(Archive & ar, Test * t, const unsigned int file_version) {
                  // retrieve data from archive required to construct new instance
                  int m;
                  ar >> m;
                  // create and load data through pointer to Parent
                  // tracking handles issues of duplicates.
                  Parent * pparent;
                  ar >> pparent;
                  // invoke inplace constructor to initialize instance of Test
                  ::new(t)Test(m, *pparent);
              }
      }}
      
      #endif
      
      #include <iostream>
      #include <sstream>
      #include <boost/serialization/serialization.hpp>
      #include <boost/archive/text_oarchive.hpp>
      #include <boost/archive/text_iarchive.hpp>
      
      int main() {
          Parent* the_instance = new RefMem;
      
          Test test = Test(50, *the_instance);
      
          std::cout << "t_: " << test.t_ << "\n";
          std::cout << "Test val: " << test.parent_.get_test_val() << "\n";
          std::ostringstream oss;
          {
              boost::archive::text_oarchive oa(oss);
              Test* p = &test;
              oa << the_instance << p; // NOTE SERIALIZE test AS-IF A POINTER
          }
      
          {
              Parent* the_cloned_instance = nullptr;
              Test* cloned = nullptr;
      
              std::istringstream iss(oss.str());
              {
                  boost::archive::text_iarchive ia(iss);
                  ia >> the_cloned_instance >> cloned;
              }
      
              std::cout << "t_: " << cloned->t_ << "\n";
              std::cout << "Test val: " << cloned->parent_.get_test_val() << "\n";
              std::cout << "Are Parent objects aliasing: " << std::boolalpha << 
                  (&cloned->parent_ == the_cloned_instance) << "\n";
          }
      }
      

      列印

      RefMem::RefMem():12342
      Test::Test(int, Parent&):12342
      t_: 50
      int Parent::get_test_val():12342
      Test val: 12342
      Test::Test(int, Parent&):12342
      t_: 50
      int Parent::get_test_val():12342
      Test val: 12342
      Are Parent objects aliasing: true
      



      :说我们想要什么



      为避免引用成员泄漏和可用性问题,让我们使用shared_ptr!

      Alternatively: say what we want

      To avoid the leakiness and the usability issues associated with reference members, let's use a shared_ptr instead!

      Live On Coliru

      #include <iostream>
      #include <boost/serialization/serialization.hpp>
      #include <boost/serialization/shared_ptr.hpp>
      #include <boost/archive/text_oarchive.hpp>
      #include <boost/archive/text_iarchive.hpp>
      #include <boost/make_shared.hpp>
      
      class Parent {
        public:
          int test_val = 1234234;
      
          int p() { return 13294; }
      
          int get_test_val() {
              std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
              return test_val;
          }
      
          template <class Archive> void serialize(Archive &ar, unsigned) {
              ar & test_val; 
          }
      };
      
      class RefMem : public Parent {
        public:
          RefMem() {
              test_val = 12342;
              std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
          }
      };
      
      using ParentRef = boost::shared_ptr<Parent>;
      
      class Test {
        public:
          int t_ = 0;
          ParentRef parent_;
      
          Test() = default;
          Test(int t, ParentRef parent) : t_(t), parent_(parent) { }
      
          template <class Archive> void serialize(Archive &ar, const unsigned int file_version) {
              ar & t_ & parent_;
          }
      };
      
      #include <sstream>
      
      int main() {
          ParentRef the_instance = boost::make_shared<RefMem>();
      
          Test test = Test(50, the_instance);
      
          std::cout << "t_: " << test.t_ << "\n";
          std::cout << "Test val: " << test.parent_->get_test_val() << "\n";
          std::ostringstream oss;
          {
              boost::archive::text_oarchive oa(oss);
              oa << the_instance << test; // NOTE SERIALIZE test AS-IF A POINTER
          }
      
          {
              ParentRef the_cloned_instance;
              Test cloned;
      
              std::istringstream iss(oss.str());
              {
                  boost::archive::text_iarchive ia(iss);
                  ia >> the_cloned_instance >> cloned;
              }
      
              std::cout << "t_: " << cloned.t_ << "\n";
              std::cout << "Test val: " << cloned.parent_->get_test_val() << "\n";
              std::cout << "Are Parent objects aliasing: " << std::boolalpha << 
                  (cloned.parent_ == the_cloned_instance) << "\n";
          }
      }
      

      请注意,没有内存泄漏,即使您不分别将 RefMem 序列化。并且对象跟踪可以与共享指针(通过 boost / serialization / shared_pointer.hpp )实现。

      Note that there is no complication anymore. No memory leaks, not even when you don't serialize the RefMem instance separately. And the object tracking works fine with shared pointers (as implemented through boost/serialization/shared_pointer.hpp).

      ¹或来自父母的任何事情,显然

      ¹ or anything else deriving from Parent, obviously

      这篇关于Boost序列化引用成员抽象类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆