首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >序列化:如何避免指针的双重保存?(并获得free.c错误)

序列化:如何避免指针的双重保存?(并获得free.c错误)
EN

Stack Overflow用户
提问于 2014-05-04 12:44:03
回答 1查看 713关注 0票数 1

我现在有一个stl:list,它包含一些基本对象和一些派生类。

我可以在没有任何问题的情况下加载和保存这个列表。与BOOST_CLASS_EXPORT(.)宏,所有内容都正常工作,直到我添加以下内容:

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

(不要再这么抽象了:这些都是“游戏对象”,它们引用了一些被称为“区域”的对象,它们都是从初级游戏类派生出来的。)

所以现在我正在序列化列表,并且每个对象都被单独序列化。

代码语言:javascript
复制
ar  & characterList;

“游戏”对象包含以下代码:

代码语言:javascript
复制
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
}

所以我试图保存到指向这个区域的指针,但是在我添加这些行之后,程序会崩溃,我会释放.c,这告诉我我释放了两次相同的指针。Boost还会给我一个未注册的类错误。(我也导出了Area类,而且它也不需要

代码语言:javascript
复制
ar & area;

)

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

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

或者我应该尝试完全不同的东西(从I列表中获取指针)

EN

回答 1

Stack Overflow用户

发布于 2014-05-04 16:02:49

好吧,我为你做了一个演示:

代码语言:javascript
复制
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")

备注

  • List拥有areas的所有权。(因此,使列表不可复制;否则复制List将导致双重删除)
  • GameObject是指List中的一个Area对象。因此,不应该删除正在销毁的区域(列表拥有它们!)

示例程序:

代码语言:javascript
复制
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";
}

您将注意到这是如何运行良好的(住在Coliru):

代码语言:javascript
复制
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

但是,您不会在Coliru上看到的是,这会泄漏内存。瓦兰告诉你,在反序列化过程中分配的4个字节丢失了--很明显,这是使用AreaGameObject

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

问得好。是的,确实如此。但是(这很容易被忽略),它只对同一对象图中的指针使用。因此,你可以通过

  1. 用Gameobject序列化列表(中断封装,容易出错)
  2. 创建一个超级对象,作为“容器”(或技术上:对象图的根)。 结构世界:boost::不可复制的{ list;GameObject* the_object;World():the_object(nullptr) {} ~ World () { delete the_object;} the_object;}私有:朋友推动::序列化::访问;模板无效序列化(存档& ar,未签名){ ar &the_object;} }; 现在我们可以序列化/反序列化整个对象图("World"),对象跟踪将如您所期望的那样工作:住在Coliru clang++ -std=c++11 -Os -Wall -pedantic main.cpp -lboost_system -lboost_serialization & ./a.out 22序列化:归档10 0 0 0 3 1 0 0 0 3 1 1 2 2 3 3 3 4 5 5 6 6 7 7 8 8 3 9 4 1 0 0 0 3 3-平等?真的

不会再有内存泄漏了!

全代码列表

代码语言:javascript
复制
#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";
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23456360

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档