升压参考成员抽象类的序列 [英] Boost serialization of reference member abstract class

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

问题描述

我想弄清楚如何序列化,我和升压放在一起的类。我马上到code:

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


#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 ++和我可以得到一个基本的例子来工作,但这样的事,我序列化基准部件是一个抽象类的子类,然后反序列化。这code只是在重复我想在另一个程序做。我有几个随机函数/变量只是用于测试。

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.

编辑:我将如何得到这个code编译并正常工作?

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

推荐答案

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

You're confused about the ownership semantics of references.


  1. 参考父_ 仅仅是点,以 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 的一个实例为点。我们可以期待加速向系列化(不知)动态实例化一个 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.

由于升压序列化序列化的的不构建这些对象,问题是如何引用甚至可以在所有被初始化。

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
}


  • 首先,构造禁止编译器生成的默认构造函数,这样一来,实际上,行测试克隆; 甚至无法编译

  • 其次,对于默认参数是一个右值引用它变得尽快构造函数返回晃来晃去。你的程序的未定义行为

  • 第三行

    • 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
      

      没有做什么,你认为它。它复制的的的对象从对象由父_ 引用。这可能是不可见,因为父_ 是同一个对象在这里,但这里甚至有对象切片涉及(<一个HREF =htt​​p://stackoverflow.com/questions/274626/what-is-object-slicing>什么是对象切片?)。

      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?).

      重新集结,并创出文档参考系列化的:


      What do?

      Best to regroup and hit the documentation for Serialization of References:

      这包含引用成员的类通常需要非默认
        当一个实例构造构造作为参照,才可以设置。
        的previous部分的例子是,如果类具有稍微更复杂
        参考成员。 这就提出了一个如何以及在何处对象的问题
        被称为存储以及如何他们创造。
      还有就是
        即将多态基类的引用问题。基本上,这些都是
        出现的关于指针同样的问题。这并不奇怪,因为
        引用是真是一种特殊的指针。

      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 来缓解<$的非默认constructibility C $ C>测试

      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)

      <大骨节病> 住在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!

      <大骨节病> 住在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 实例。和对象跟踪正常工作与共享指针(通过实施升压/系列化/ 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

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

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