C++ 序列化:如何避免指针的双重保存?(并获得一个免费的.c错误)

C++ 序列化:如何避免指针的双重保存?(并获得一个免费的.c错误),c++,boost-serialization,C++,Boost Serialization,我目前有一个stl:list,其中包含一些基本对象和一些派生类 我可以毫无问题地加载和保存此列表。使用BOOST_CLASS_EXPORT(…)宏,一切都正常工作,直到我添加以下内容: -------------------------------------------------------------------------------------- 我需要一些包含列表中其他对象指针的对象 (为了避免过于抽象:这些是一些“游戏对象”,它们对称为“区域”的对象有一些引用,它们都是从基本游戏类

我目前有一个stl:list,其中包含一些基本对象和一些派生类

我可以毫无问题地加载和保存此列表。使用BOOST_CLASS_EXPORT(…)宏,一切都正常工作,直到我添加以下内容:


--------------------------------------------------------------------------------------

我需要一些包含列表中其他对象指针的对象

(为了避免过于抽象:这些是一些“游戏对象”,它们对称为“区域”的对象有一些引用,它们都是从基本游戏类派生的。)
--------------------------------------------------------------------------------------


现在我正在序列化列表,每个对象都会被单独序列化

ar  & characterList;
游戏对象包含以下代码:

template<class Archive>
void save(Archive & ar, const unsigned int version) const {
    ar & boost::serialization::base_object<M_Character>(*this);
    ar & area; // If I add this line, it will crash
}

template<class Archive>
void load(Archive & ar, const unsigned int version) const {
    ar & boost::serialization::base_object<M_Character>(*this);
    ar & area; // This one as well
}
)

因此该区域将被序列化两次,因为首先该区域将从列表中保存,然后从对象中保存

我怎样才能避免这种情况?是否可以第一次保存整个对象,第二次只保存指针


或者我应该尝试完全不同的方法(从带有ID的列表中获取指针)

好的,我已经为您创建了一个演示:

struct Area
{
    Area(int i):id(i) {}
    int id;
};

struct List : boost::noncopyable
{
    std::vector<Area*> areas;

    ~List() {
        std::for_each(areas.begin(), areas.end(), std::default_delete<Area>());
    }
};

struct M_Character {
    virtual ~M_Character() {} 
};

struct GameObject : M_Character, boost::noncopyable
{
    Area* area;

    GameObject(Area* area = nullptr) : area(area) {}
};

BOOST_CLASS_EXPORT_GUID(GameObject, "GameObject")
您将注意到它如何运行良好():

不过,你在Coliru上看不到的是,这会泄露内存。Valgrind告诉您在反序列化过程中分配的4个字节丢失-显然这是
游戏对象
区域

但是嘿!在序列化指针时,Boost序列化不是要执行对象跟踪吗

好问题。是的但是(这很容易忽略),它将只对同一对象图中的指针执行。因此,您可以通过

  • 用游戏对象序列化列表(破坏封装,容易出错)
  • 制作一个用作“容器”的超级对象(或者从技术上说:对象图的根)

  • 没有更多的内存泄漏

    完整代码清单
    #包括
    #包括
    #包括
    #包括
    #包括
    结构区
    {
    区域(inti):id(i){}
    int-id;
    私人:
    Area(){}//仅用于反序列化
    friend boost::serialization::access;
    模板
    无效序列化(存档&ar,未签名){ar&id;}
    };
    结构列表:boost::不可复制
    {
    病媒区;
    ~List(){
    std::for_each(areas.begin()、areas.end()、std::default_delete());
    }
    私人:
    friend boost::serialization::access;
    模板
    无效序列化(存档&ar,未签名){ar&AREA;}
    };
    结构M_字符{
    虚拟~M_字符(){}
    私人:
    friend boost::serialization::access;
    模板
    无效序列化(存档&/*ar*/,未签名){}
    };
    结构游戏对象:M_字符,boost::不可复制
    {
    面积*面积;
    游戏对象(Area*Area=nullptr):区域(Area){}
    私人:
    friend boost::serialization::access;
    模板
    无效序列化(存档(&R),未签名){
    ar&boost::serialization::base_对象(*this);
    ar&area;
    }
    };
    BOOST_CLASS_EXPORT_GUID(游戏对象,“游戏对象”)
    #包括
    结构世界:boost::不可复制
    {
    名单;
    GameObject*_对象;
    World():_对象(nullptr){
    ~World(){删除_对象;}
    私人:
    friend boost::serialization::access;
    模板
    无效序列化(存档(&R),未签名){
    应收账款清单;
    ar&u对象;
    }
    };
    字符串序列化(世界常量(&w)
    {
    std::stringstream-ss;
    boost::archive::text\u oarchive oa(ss);
    oa>w;
    }
    int main()
    {
    世界;
    对于(int i=0;i<10;++i)
    世界。列表。区域。推回(新区域(i));
    //构建原始obj
    world.the_object=新游戏对象(world.list.areas[3]);//共享列表中的区域指针
    std::string const serialized=serialize(世界);
    
    std::难道信息太少了吗。你能把它做成SSCCE吗?嗯,这可能需要一些时间,因为我自己的库很大。无论如何,我会试试。-一个带有.lib和头文件的小演示程序足够了吗?嗯。这不是独立的,但你可以试试我。我通常不会花太多时间模拟/存根不可用的部分。我e为您创建了一个SSCCE,简化为一个区域列表和一个游戏对象。如果它还没有回答,那么至少它将帮助您获得一个SSCCE:)哇!非常感谢!我有一个包含stl::list的世界级(与本示例中的stl\uu向量结构不同)但这不应该是问题所在-似乎我错过了boost::noncopyable在我的世界级中,我会尝试一下!太棒了!!!谢谢!!似乎现在它开始工作了…我从来没有想过向世界添加基类…:)@MrMonday从逻辑上讲,仅仅添加基类不应该改变行为(尽管它可以防止违反“三人法则”,而在《准则》的其他部分,这可能是导致您之前看到的问题的原因)
    struct Area
    {
        Area(int i):id(i) {}
        int id;
    };
    
    struct List : boost::noncopyable
    {
        std::vector<Area*> areas;
    
        ~List() {
            std::for_each(areas.begin(), areas.end(), std::default_delete<Area>());
        }
    };
    
    struct M_Character {
        virtual ~M_Character() {} 
    };
    
    struct GameObject : M_Character, boost::noncopyable
    {
        Area* area;
    
        GameObject(Area* area = nullptr) : area(area) {}
    };
    
    BOOST_CLASS_EXPORT_GUID(GameObject, "GameObject")
    
    int main()
    {
        List l;
        for (int i = 0; i < 10; ++i)
            l.areas.push_back(new Area(i));
    
        std::unique_ptr<M_Character> obj, roundtrip;
    
        // build original obj
        obj.reset(new GameObject(l.areas[3])); // sharing the area pointer from the list
    
        std::string const serialized = serialize(obj.get());
        std::cout << serialized << '\n';
        std::cout << "-------------------------------------------------\n";
    
        // create roundtrip
        roundtrip.reset(deserialize(serialized));
        std::cout << "EQUAL? " << std::boolalpha << (serialized == serialize(roundtrip.get())) << "\n";
    }
    
    clang++ -std=c++11 -Os -Wall -pedantic main.cpp -lboost_system -lboost_serialization && ./a.out
    22 serialization::archive 10 1 10 GameObject 1 0
    0 1 0
    1 2 1 0
    2 3
    -------------------------------------------------
    EQUAL? true
    
    struct World : boost::noncopyable
    {
        List list;
        GameObject* the_object;
    
        World() : the_object(nullptr) {}
        ~World() { delete the_object; }
    
    private:
        friend boost::serialization::access;
        template<class Archive>
            void serialize(Archive & ar, unsigned) {
                ar & list;
                ar & the_object;
            }
    };
    
    clang++ -std=c++11 -Os -Wall -pedantic main.cpp -lboost_system -lboost_serialization   && ./a.out
    22 serialization::archive 10 0 0 0 0 0 0 10 0 3 1 0
    0 0 3
    1 1 3
    2 2 3
    3 3 3
    4 4 3
    5 5 3
    6 6 3
    7 7 3
    8 8 3
    9 9 4 1 0
    10 0 0 3 3
    -------------------------------------------------
    EQUAL? true
    
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/serialization/serialization.hpp>
    #include <boost/serialization/export.hpp>
    #include <boost/serialization/vector.hpp>
    
    struct Area
    {
        Area(int i):id(i) {}
        int id;
      private:
        Area() { } // used only in deserialization
        friend boost::serialization::access;
        template<class Archive>
            void serialize(Archive & ar, unsigned) { ar & id; }
    };
    
    struct List : boost::noncopyable
    {
        std::vector<Area*> areas;
    
        ~List() {
            std::for_each(areas.begin(), areas.end(), std::default_delete<Area>());
        }
    
      private:
        friend boost::serialization::access;
        template<class Archive>
            void serialize(Archive & ar, unsigned) { ar & areas; }
    };
    
    struct M_Character {
        virtual ~M_Character() {} 
      private:
        friend boost::serialization::access;
        template<class Archive>
            void serialize(Archive & /*ar*/, unsigned) { }
    };
    
    struct GameObject : M_Character, boost::noncopyable
    {
        Area* area;
    
        GameObject(Area* area = nullptr) : area(area) {}
    
      private:
        friend boost::serialization::access;
        template<class Archive>
            void serialize(Archive & ar, unsigned) {
                ar & boost::serialization::base_object<M_Character>(*this);
                ar & area;
            }
    };
    
    BOOST_CLASS_EXPORT_GUID(GameObject, "GameObject")
    #include <sstream>
    
            struct World : boost::noncopyable
            {
                List list;
                GameObject* the_object;
    
                World() : the_object(nullptr) {}
                ~World() { delete the_object; }
    
            private:
                friend boost::serialization::access;
                template<class Archive>
                    void serialize(Archive & ar, unsigned) {
                        ar & list;
                        ar & the_object;
                    }
            };
    
    std::string serialize(World const& w)
    {
        std::stringstream ss;
        boost::archive::text_oarchive oa(ss);
    
        oa << w;
    
        return ss.str();
    }
    
    void deserialize(std::string const& input, World& w)
    {
        std::stringstream ss(input);
        boost::archive::text_iarchive ia(ss);
    
        ia >> w;
    }
    
    int main()
    {
        World world;
        for (int i = 0; i < 10; ++i)
            world.list.areas.push_back(new Area(i));
    
        // build original obj
        world.the_object = new GameObject(world.list.areas[3]); // sharing the area pointer from the list
    
        std::string const serialized = serialize(world);
        std::cout << serialized << '\n';
        std::cout << "-------------------------------------------------\n";
    
        // create roundtrip
        World roundtrip;
        deserialize(serialized, roundtrip);
        std::cout << "EQUAL? " << std::boolalpha << (serialized == serialize(roundtrip)) << "\n";
    }