C++ 提升引用成员抽象类的序列化

C++ 提升引用成员抽象类的序列化,c++,boost,boost-serialization,C++,Boost,Boost Serialization,我试图弄清楚如何序列化我与Boost组合在一起的类。我将直接进入代码: #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>

我试图弄清楚如何序列化我与Boost组合在一起的类。我将直接进入代码:

#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
\ifndef测试_
#定义测试_
#包括
#包括
#包括
#包括
班级家长
{
公众:
int test_val=1234234;
int p()
{
返回13294;
}
int get_test_val()
{

std::cout您对引用的所有权语义感到困惑

  • 引用
    parent \
    仅“指向”一个
    RefMem
    的实例。当您序列化时,编写这些引用“很容易”(因为它们是左值引用,值本身将被序列化)

    然而,对于反序列化,事情并不是那么简单,因为我们没有指向的
    MemRef
    实例凭空动态实例化一个
    MemRef
    ,并静默地将引用点指向它。但是,这最多会导致内存泄漏

  • 引用成员还有一点特别重要,引用成员只能在构造函数的初始值设定项列表中初始化

    因为Boost序列化序列化了值,所以它不会构造这些对象,问题是引用如何初始化

    您当前的构造函数存在许多相关问题:

    Test(int t, Parent && parent = RefMem()) : parent_(parent) {
        std::cout << __FUNCTION__ << ":" << this->parent_.test_val << "\n";
        t_      = t;
        parent_ = parent; // OOPS! TODO FIXME
    }
    
    与您认为的不同。它将
    父对象的值从
    父对象复制到
    父对象
    引用的对象上。这可能不可见,因为
    父对象
    父对象
    在这里是同一个对象,但甚至涉及到对象切片()


  • 你怎么办? 最好重新组合并点击文档:

    包含引用成员的类通常需要非默认值 构造函数作为引用只能在构造实例时设置。 如果类具有 引用成员。这就提出了一个问题,即对象如何以及在哪里 被引用的内容被存储,以及它们是如何创建的。还有 关于多态基类的引用的问题。基本上,这些是 关于指针也会出现同样的问题,这一点也不奇怪 引用实际上是一种特殊的指针

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

    (强调矿山)

    文档确实建议
    加载构造数据
    /
    保存构造数据
    ,以减轻
    测试
    的非默认构造性

    请注意,他们建议将引用成员作为指针处理,这似乎不错,但只有当实际指向的对象也通过同一个存档中的指针进行序列化时,才有意义。在这种情况下,将发现别名指针并避免创建重复实例

    如果没有,您仍然会有内存泄漏,并且可能会破坏程序状态

    使用
    加载/保存构造数据的演示
    这里是上述技术的一个演示。请注意,我们正在泄漏动态分配的对象。我不喜欢这种样式,因为它本质上将引用视为指针

    如果这是我们想要的,我们应该考虑使用指针(见下文)

    #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";
        }
    }
    
    #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";
        }
    }
    
    或者:说出我们想要的 为了避免与引用成员相关的泄漏和可用性问题,让我们改用共享的\u ptr

    #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";
        }
    }
    
    #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";
        }
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    班级家长{
    公众:
    int test_val=1234234;
    int p(){return 13294;}
    int get_test_val(){
    
    std::cout添加了两个活样本,两个都添加了一个,避免了所有的陷阱。答案很好。我将不得不读几遍。我使用双安培的原因之一是来自这里的一个建议:…是否有一个干净的(无泄漏)根据您在此处所做的操作,将默认值的功能获取到抽象参数的方法?我注意到您忽略了它。关于
    父项&&
    :很好的观点。我对他的回答发表了评论,他在那里犯了一个错误。关于默认值:我没有忽略它。没有抽象参数这样的东西。(您可能指的是对多态基的引用)。在我的“理智方法”中,默认值只是一个空的共享_指针,但请随意给它一个默认值,因为智能指针将进行清理。这是一个非常好用的方法。因此有多个答案,文档中也有:
    #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";
        }
    }