我目前有一个stl:list,它包含一些基础对象和一些派生类。
我可以毫无问题地加载和保存此列表。使用BOOST_CLASS_EXPORT(...)宏,一切正常,直到我添加以下内容:
------------------------------------------- -------------------------------------------
我需要一些包含列表中其他对象指针的对象。
(为了停止如此抽象:这些是#34;游戏对象"他们有一些对象被称为"区域",两者都来自基本的游戏类。 )
---------------------------------------------- ----------------------------------------
所以现在我正在序列化列表,并且每个对象都被单独序列化。
ar & characterList;
Game-Object包含以下代码:
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
}
所以我试图保存到指向区域的指针,但是在我添加这些行之后,程序将崩溃,我将转到free.c,它告诉我,我释放了两次相同的指针。 Boost还会给我一个未注册的类错误。 (Altough我导出了Area Class,它也没有
ar & area;
)
因此,区域将被序列化两次,因为首先区域将从列表中保存,然后从对象中保存。
我该如何避免这种情况?有可能第一次保存整个对象而第二次只保存指针吗?
或者我应该尝试完全不同的东西(从带有ID的列表中获取指针)
答案 0 :(得分:1)
好的,我已经为您创建了一个演示:
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")
请注意
areas
的所有权。 (因此,使列表不可复制;否则复制List
将导致双删除)Area
中的List
个对象之一。因此不应该删除销毁区域(列表拥有它们!)示例程序:
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";
}
你会注意到它如何正常运行( Live On Coliru ):
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上看到的是,这会泄漏内存。 Valgrind告诉你,在反序列化过程中丢失了4个字节 - 显然这是Area
来自GameObject
。
但是嘿!在序列化指针时,是否提升序列化以进行对象跟踪?
好问题。是的,它确实。 但(这很容易被忽略),仅对同一对象图中的指针执行此操作。因此,您可以通过
解决问题制作一个充当&#34;容器的超级对象&#34; (或技术上:对象图的根)
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;
}
};
现在我们可以序列化/反序列化整个对象图(&#34; World&#34;),对象跟踪将按预期工作: Live On Coliru
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";
}